mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking changes from David S Miller: 1) Remove the ipv4 routing cache. Now lookups go directly into the FIB trie and use prebuilt routes cached there. No more garbage collection, no more rDOS attacks on the routing cache. Instead we now get predictable and consistent performance, no matter what the pattern of traffic we service. This has been almost 2 years in the making. Special thanks to Julian Anastasov, Eric Dumazet, Steffen Klassert, and others who have helped along the way. I'm sure that with a change of this magnitude there will be some kind of fallout, but such things ought the be simple to fix at this point. Luckily I'm not European so I'll be around all of August to fix things :-) The major stages of this work here are each fronted by a forced merge commit whose commit message contains a top-level description of the motivations and implementation issues. 2) Pre-demux of established ipv4 TCP sockets, saves a route demux on input. 3) TCP SYN/ACK performance tweaks from Eric Dumazet. 4) Add namespace support for netfilter L4 conntrack helpers, from Gao Feng. 5) Add config mechanism for Energy Efficient Ethernet to ethtool, from Yuval Mintz. 6) Remove quadratic behavior from /proc/net/unix, from Eric Dumazet. 7) Support for connection tracker helpers in userspace, from Pablo Neira Ayuso. 8) Allow userspace driven TX load balancing functions in TEAM driver, from Jiri Pirko. 9) Kill off NLMSG_PUT and RTA_PUT macros, more gross stuff with embedded gotos. 10) TCP Small Queues, essentially minimize the amount of TCP data queued up in the packet scheduler layer. Whereas the existing BQL (Byte Queue Limits) limits the pkt_sched --> netdevice queuing levels, this controls the TCP --> pkt_sched queueing levels. From Eric Dumazet. 11) Reduce the number of get_page/put_page ops done on SKB fragments, from Alexander Duyck. 12) Implement protection against blind resets in TCP (RFC 5961), from Eric Dumazet. 13) Support the client side of TCP Fast Open, basically the ability to send data in the SYN exchange, from Yuchung Cheng. Basically, the sender queues up data with a sendmsg() call using MSG_FASTOPEN, then they do the connect() which emits the queued up fastopen data. 14) Avoid all the problems we get into in TCP when timers or PMTU events hit a locked socket. The TCP Small Queues changes added a tcp_release_cb() that allows us to queue work up to the release_sock() caller, and that's what we use here too. From Eric Dumazet. 15) Zero copy on TX support for TUN driver, from Michael S. Tsirkin. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1870 commits) genetlink: define lockdep_genl_is_held() when CONFIG_LOCKDEP r8169: revert "add byte queue limit support". ipv4: Change rt->rt_iif encoding. net: Make skb->skb_iif always track skb->dev ipv4: Prepare for change of rt->rt_iif encoding. ipv4: Remove all RTCF_DIRECTSRC handliing. ipv4: Really ignore ICMP address requests/replies. decnet: Don't set RTCF_DIRECTSRC. net/ipv4/ip_vti.c: Fix __rcu warnings detected by sparse. ipv4: Remove redundant assignment rds: set correct msg_namelen openvswitch: potential NULL deref in sample() tcp: dont drop MTU reduction indications bnx2x: Add new 57840 device IDs tcp: avoid oops in tcp_metrics and reset tcpm_stamp niu: Change niu_rbr_fill() to use unlikely() to check niu_rbr_add_page() return value niu: Fix to check for dma mapping errors. net: Fix references to out-of-scope variables in put_cmsg_compat() net: ethernet: davinci_emac: add pm_runtime support net: ethernet: davinci_emac: Remove unnecessary #include ...
This commit is contained in:
commit
3c4cfadef6
@ -404,7 +404,6 @@
|
||||
!Finclude/net/mac80211.h ieee80211_get_tkip_p1k
|
||||
!Finclude/net/mac80211.h ieee80211_get_tkip_p1k_iv
|
||||
!Finclude/net/mac80211.h ieee80211_get_tkip_p2k
|
||||
!Finclude/net/mac80211.h ieee80211_key_removed
|
||||
</chapter>
|
||||
|
||||
<chapter id="powersave">
|
||||
|
@ -69,9 +69,13 @@ static int cn_test_want_notify(void)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nlh = NLMSG_PUT(skb, 0, 0x123, NLMSG_DONE, size - sizeof(*nlh));
|
||||
nlh = nlmsg_put(skb, 0, 0x123, NLMSG_DONE, size - sizeof(*nlh), 0);
|
||||
if (!nlh) {
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
msg = (struct cn_msg *)NLMSG_DATA(nlh);
|
||||
msg = nlmsg_data(nlh);
|
||||
|
||||
memset(msg, 0, size0);
|
||||
|
||||
@ -117,11 +121,6 @@ static int cn_test_want_notify(void)
|
||||
pr_info("request was sent: group=0x%x\n", ctl->group);
|
||||
|
||||
return 0;
|
||||
|
||||
nlmsg_failure:
|
||||
pr_err("failed to send %u.%u\n", msg->seq, msg->ack);
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
29
Documentation/devicetree/bindings/net/broadcom-bcm87xx.txt
Normal file
29
Documentation/devicetree/bindings/net/broadcom-bcm87xx.txt
Normal file
@ -0,0 +1,29 @@
|
||||
The Broadcom BCM87XX devices are a family of 10G Ethernet PHYs. They
|
||||
have these bindings in addition to the standard PHY bindings.
|
||||
|
||||
Compatible: Should contain "broadcom,bcm8706" or "broadcom,bcm8727" and
|
||||
"ethernet-phy-ieee802.3-c45"
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- broadcom,c45-reg-init : one of more sets of 4 cells. The first cell
|
||||
is the MDIO Manageable Device (MMD) address, the second a register
|
||||
address within the MMD, the third cell contains a mask to be ANDed
|
||||
with the existing register value, and the fourth cell is ORed with
|
||||
he result to yield the new register value. If the third cell has a
|
||||
value of zero, no read of the existing value is performed.
|
||||
|
||||
Example:
|
||||
|
||||
ethernet-phy@5 {
|
||||
reg = <5>;
|
||||
compatible = "broadcom,bcm8706", "ethernet-phy-ieee802.3-c45";
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <12 8>; /* Pin 12, active low */
|
||||
/*
|
||||
* Set PMD Digital Control Register for
|
||||
* GPIO[1] Tx/Rx
|
||||
* GPIO[0] R64 Sync Acquired
|
||||
*/
|
||||
broadcom,c45-reg-init = <1 0xc808 0xff8f 0x70>;
|
||||
};
|
@ -11,6 +11,9 @@ Required properties:
|
||||
|
||||
- reg : Offset and length of the register set for this device
|
||||
- interrupts : Interrupt tuple for this device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- clock-frequency : The oscillator frequency driving the flexcan device
|
||||
|
||||
Example:
|
||||
|
41
Documentation/devicetree/bindings/net/davinci_emac.txt
Normal file
41
Documentation/devicetree/bindings/net/davinci_emac.txt
Normal file
@ -0,0 +1,41 @@
|
||||
* Texas Instruments Davinci EMAC
|
||||
|
||||
This file provides information, what the device node
|
||||
for the davinci_emac interface contains.
|
||||
|
||||
Required properties:
|
||||
- compatible: "ti,davinci-dm6467-emac";
|
||||
- reg: Offset and length of the register set for the device
|
||||
- ti,davinci-ctrl-reg-offset: offset to control register
|
||||
- ti,davinci-ctrl-mod-reg-offset: offset to control module register
|
||||
- ti,davinci-ctrl-ram-offset: offset to control module ram
|
||||
- ti,davinci-ctrl-ram-size: size of control module ram
|
||||
- ti,davinci-rmii-en: use RMII
|
||||
- ti,davinci-no-bd-ram: has the emac controller BD RAM
|
||||
- phy-handle: Contains a phandle to an Ethernet PHY.
|
||||
if not, davinci_emac driver defaults to 100/FULL
|
||||
- interrupts: interrupt mapping for the davinci emac interrupts sources:
|
||||
4 sources: <Receive Threshold Interrupt
|
||||
Receive Interrupt
|
||||
Transmit Interrupt
|
||||
Miscellaneous Interrupt>
|
||||
|
||||
Optional properties:
|
||||
- local-mac-address : 6 bytes, mac address
|
||||
|
||||
Example (enbw_cmc board):
|
||||
eth0: emac@1e20000 {
|
||||
compatible = "ti,davinci-dm6467-emac";
|
||||
reg = <0x220000 0x4000>;
|
||||
ti,davinci-ctrl-reg-offset = <0x3000>;
|
||||
ti,davinci-ctrl-mod-reg-offset = <0x2000>;
|
||||
ti,davinci-ctrl-ram-offset = <0>;
|
||||
ti,davinci-ctrl-ram-size = <0x2000>;
|
||||
local-mac-address = [ 00 00 00 00 00 00 ];
|
||||
interrupts = <33
|
||||
34
|
||||
35
|
||||
36
|
||||
>;
|
||||
interrupt-parent = <&intc>;
|
||||
};
|
@ -7,10 +7,14 @@ Required properties:
|
||||
- phy-mode : String, operation mode of the PHY interface.
|
||||
Supported values are: "mii", "gmii", "sgmii", "tbi", "rmii",
|
||||
"rgmii", "rgmii-id", "rgmii-rxid", "rgmii-txid", "rtbi", "smii".
|
||||
- phy-reset-gpios : Should specify the gpio for phy reset
|
||||
|
||||
Optional properties:
|
||||
- local-mac-address : 6 bytes, mac address
|
||||
- phy-reset-gpios : Should specify the gpio for phy reset
|
||||
- phy-reset-duration : Reset duration in milliseconds. Should present
|
||||
only if property "phy-reset-gpios" is available. Missing the property
|
||||
will have the duration be 1 millisecond. Numbers greater than 1000 are
|
||||
invalid and 1 millisecond will be used instead.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -14,10 +14,20 @@ Required properties:
|
||||
- linux,phandle : phandle for this node; likely referenced by an
|
||||
ethernet controller node.
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- compatible: Compatible list, may contain
|
||||
"ethernet-phy-ieee802.3-c22" or "ethernet-phy-ieee802.3-c45" for
|
||||
PHYs that implement IEEE802.3 clause 22 or IEEE802.3 clause 45
|
||||
specifications. If neither of these are specified, the default is to
|
||||
assume clause 22. The compatible list may also contain other
|
||||
elements.
|
||||
|
||||
Example:
|
||||
|
||||
ethernet-phy@0 {
|
||||
linux,phandle = <2452000>
|
||||
compatible = "ethernet-phy-ieee802.3-c22";
|
||||
linux,phandle = <2452000>;
|
||||
interrupt-parent = <40000>;
|
||||
interrupts = <35 1>;
|
||||
reg = <0>;
|
||||
|
@ -1,7 +1,8 @@
|
||||
* STMicroelectronics 10/100/1000 Ethernet driver (GMAC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "st,spear600-gmac"
|
||||
- compatible: Should be "snps,dwmac-<ip_version>" "snps,dwmac"
|
||||
For backwards compatibility: "st,spear600-gmac" is also supported.
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupt-parent: Should be the phandle for the interrupt controller
|
||||
that services interrupts for this device
|
||||
|
@ -249,15 +249,6 @@ Who: Ravikiran Thirumalai <kiran@scalex86.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Code that is now under CONFIG_WIRELESS_EXT_SYSFS
|
||||
(in net/core/net-sysfs.c)
|
||||
When: 3.5
|
||||
Why: Over 1K .text/.data size reduction, data is available in other
|
||||
ways (ioctls)
|
||||
Who: Johannes Berg <johannes@sipsolutions.net>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: sysfs ui for changing p4-clockmod parameters
|
||||
When: September 2009
|
||||
Why: See commits 129f8ae9b1b5be94517da76009ea956e89104ce8 and
|
||||
@ -414,21 +405,6 @@ Who: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: xt_connlimit rev 0
|
||||
When: 2012
|
||||
Who: Jan Engelhardt <jengelh@medozas.de>
|
||||
Files: net/netfilter/xt_connlimit.c
|
||||
|
||||
----------------------------
|
||||
|
||||
What: ipt_addrtype match include file
|
||||
When: 2012
|
||||
Why: superseded by xt_addrtype
|
||||
Who: Florian Westphal <fw@strlen.de>
|
||||
Files: include/linux/netfilter_ipv4/ipt_addrtype.h
|
||||
|
||||
----------------------------
|
||||
|
||||
What: i2c_driver.attach_adapter
|
||||
i2c_driver.detach_adapter
|
||||
When: September 2011
|
||||
@ -449,6 +425,19 @@ Who: Hans Verkuil <hans.verkuil@cisco.com>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: CONFIG_CFG80211_WEXT
|
||||
When: as soon as distributions ship new wireless tools, ie. wpa_supplicant 1.0
|
||||
and NetworkManager/connman/etc. that are able to use nl80211
|
||||
Why: Wireless extensions are deprecated, and userland tools are moving to
|
||||
using nl80211. New drivers are no longer using wireless extensions,
|
||||
and while there might still be old drivers, both new drivers and new
|
||||
userland no longer needs them and they can't be used for an feature
|
||||
developed in the past couple of years. As such, compatibility with
|
||||
wireless extensions in new drivers will be removed.
|
||||
Who: Johannes Berg <johannes@sipsolutions.net>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: g_file_storage driver
|
||||
When: 3.8
|
||||
Why: This driver has been superseded by g_mass_storage.
|
||||
@ -589,6 +578,13 @@ Why: Remount currently allows changing bound subsystems and
|
||||
|
||||
----------------------------
|
||||
|
||||
What: xt_recent rev 0
|
||||
When: 2013
|
||||
Who: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Files: net/netfilter/xt_recent.c
|
||||
|
||||
----------------------------
|
||||
|
||||
What: KVM debugfs statistics
|
||||
When: 2013
|
||||
Why: KVM tracepoints provide mostly equivalent information in a much more
|
||||
|
@ -211,6 +211,11 @@ The debug output can be changed at runtime using the file
|
||||
|
||||
will enable debug messages for when routes change.
|
||||
|
||||
Counters for different types of packets entering and leaving the
|
||||
batman-adv module are available through ethtool:
|
||||
|
||||
# ethtool --statistics bat0
|
||||
|
||||
|
||||
BATCTL
|
||||
------
|
||||
|
@ -1210,7 +1210,7 @@ options, you may wish to use the "max_bonds" module parameter,
|
||||
documented above.
|
||||
|
||||
To create multiple bonding devices with differing options, it is
|
||||
preferrable to use bonding parameters exported by sysfs, documented in the
|
||||
preferable to use bonding parameters exported by sysfs, documented in the
|
||||
section below.
|
||||
|
||||
For versions of bonding without sysfs support, the only means to
|
||||
@ -1950,7 +1950,7 @@ access to fail over to. Additionally, the bonding load balance modes
|
||||
support link monitoring of their members, so if individual links fail,
|
||||
the load will be rebalanced across the remaining devices.
|
||||
|
||||
See Section 13, "Configuring Bonding for Maximum Throughput"
|
||||
See Section 12, "Configuring Bonding for Maximum Throughput"
|
||||
for information on configuring bonding with one peer device.
|
||||
|
||||
11.2 High Availability in a Multiple Switch Topology
|
||||
@ -2620,7 +2620,7 @@ be found at:
|
||||
|
||||
https://lists.sourceforge.net/lists/listinfo/bonding-devel
|
||||
|
||||
Discussions regarding the developpement of the bonding driver take place
|
||||
Discussions regarding the development of the bonding driver take place
|
||||
on the main Linux network mailing list, hosted at vger.kernel.org. The list
|
||||
address is:
|
||||
|
||||
|
@ -1,7 +1,14 @@
|
||||
In order to use the Ethernet bridging functionality, you'll need the
|
||||
userspace tools. These programs and documentation are available
|
||||
at http://www.linuxfoundation.org/en/Net:Bridge. The download page is
|
||||
http://prdownloads.sourceforge.net/bridge.
|
||||
userspace tools.
|
||||
|
||||
Documentation for Linux bridging is on:
|
||||
http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge
|
||||
|
||||
The bridge-utilities are maintained at:
|
||||
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/bridge-utils.git
|
||||
|
||||
Additionally, the iproute2 utilities can be used to configure
|
||||
bridge devices.
|
||||
|
||||
If you still have questions, don't hesitate to post to the mailing list
|
||||
(more info https://lists.linux-foundation.org/mailman/listinfo/bridge).
|
||||
|
@ -19,60 +19,36 @@ and host. Currently, UART and Loopback are available for Linux.
|
||||
Architecture:
|
||||
------------
|
||||
The implementation of CAIF is divided into:
|
||||
* CAIF Socket Layer, Kernel API, and Net Device.
|
||||
* CAIF Socket Layer and GPRS IP Interface.
|
||||
* CAIF Core Protocol Implementation
|
||||
* CAIF Link Layer, implemented as NET devices.
|
||||
|
||||
|
||||
RTNL
|
||||
!
|
||||
! +------+ +------+ +------+
|
||||
! +------+! +------+! +------+!
|
||||
! ! Sock !! !Kernel!! ! Net !!
|
||||
! ! API !+ ! API !+ ! Dev !+ <- CAIF Client APIs
|
||||
! +------+ +------! +------+
|
||||
! ! ! !
|
||||
! +----------!----------+
|
||||
! +------+ <- CAIF Protocol Implementation
|
||||
+-------> ! CAIF !
|
||||
! Core !
|
||||
+------+
|
||||
+--------!--------+
|
||||
! !
|
||||
+------+ +-----+
|
||||
! ! ! TTY ! <- Link Layer (Net Devices)
|
||||
+------+ +-----+
|
||||
! +------+ +------+
|
||||
! +------+! +------+!
|
||||
! ! IP !! !Socket!!
|
||||
+-------> !interf!+ ! API !+ <- CAIF Client APIs
|
||||
! +------+ +------!
|
||||
! ! !
|
||||
! +-----------+
|
||||
! !
|
||||
! +------+ <- CAIF Core Protocol
|
||||
! ! CAIF !
|
||||
! ! Core !
|
||||
! +------+
|
||||
! +----------!---------+
|
||||
! ! ! !
|
||||
! +------+ +-----+ +------+
|
||||
+--> ! HSI ! ! TTY ! ! USB ! <- Link Layer (Net Devices)
|
||||
+------+ +-----+ +------+
|
||||
|
||||
|
||||
Using the Kernel API
|
||||
----------------------
|
||||
The Kernel API is used for accessing CAIF channels from the
|
||||
kernel.
|
||||
The user of the API has to implement two callbacks for receive
|
||||
and control.
|
||||
The receive callback gives a CAIF packet as a SKB. The control
|
||||
callback will
|
||||
notify of channel initialization complete, and flow-on/flow-
|
||||
off.
|
||||
|
||||
|
||||
struct caif_device caif_dev = {
|
||||
.caif_config = {
|
||||
.name = "MYDEV"
|
||||
.type = CAIF_CHTY_AT
|
||||
}
|
||||
.receive_cb = my_receive,
|
||||
.control_cb = my_control,
|
||||
};
|
||||
caif_add_device(&caif_dev);
|
||||
caif_transmit(&caif_dev, skb);
|
||||
|
||||
See the caif_kernel.h for details about the CAIF kernel API.
|
||||
|
||||
|
||||
I M P L E M E N T A T I O N
|
||||
===========================
|
||||
===========================
|
||||
|
||||
|
||||
CAIF Core Protocol Layer
|
||||
=========================================
|
||||
@ -88,17 +64,13 @@ The Core CAIF implementation contains:
|
||||
- Simple implementation of CAIF.
|
||||
- Layered architecture (a la Streams), each layer in the CAIF
|
||||
specification is implemented in a separate c-file.
|
||||
- Clients must implement PHY layer to access physical HW
|
||||
with receive and transmit functions.
|
||||
- Clients must call configuration function to add PHY layer.
|
||||
- Clients must implement CAIF layer to consume/produce
|
||||
CAIF payload with receive and transmit functions.
|
||||
- Clients must call configuration function to add and connect the
|
||||
Client layer.
|
||||
- When receiving / transmitting CAIF Packets (cfpkt), ownership is passed
|
||||
to the called function (except for framing layers' receive functions
|
||||
or if a transmit function returns an error, in which case the caller
|
||||
must free the packet).
|
||||
to the called function (except for framing layers' receive function)
|
||||
|
||||
Layered Architecture
|
||||
--------------------
|
||||
@ -109,11 +81,6 @@ Implementation. The support functions include:
|
||||
CAIF Packet has functions for creating, destroying and adding content
|
||||
and for adding/extracting header and trailers to protocol packets.
|
||||
|
||||
- CFLST CAIF list implementation.
|
||||
|
||||
- CFGLUE CAIF Glue. Contains OS Specifics, such as memory
|
||||
allocation, endianness, etc.
|
||||
|
||||
The CAIF Protocol implementation contains:
|
||||
|
||||
- CFCNFG CAIF Configuration layer. Configures the CAIF Protocol
|
||||
@ -128,7 +95,7 @@ The CAIF Protocol implementation contains:
|
||||
control and remote shutdown requests.
|
||||
|
||||
- CFVEI CAIF VEI layer. Handles CAIF AT Channels on VEI (Virtual
|
||||
External Interface). This layer encodes/decodes VEI frames.
|
||||
External Interface). This layer encodes/decodes VEI frames.
|
||||
|
||||
- CFDGML CAIF Datagram layer. Handles CAIF Datagram layer (IP
|
||||
traffic), encodes/decodes Datagram frames.
|
||||
@ -170,7 +137,7 @@ The CAIF Protocol implementation contains:
|
||||
+---------+ +---------+
|
||||
! !
|
||||
+---------+ +---------+
|
||||
| | | Serial |
|
||||
| | | Serial |
|
||||
| | | CFSERL |
|
||||
+---------+ +---------+
|
||||
|
||||
@ -186,24 +153,20 @@ In this layered approach the following "rules" apply.
|
||||
layer->dn->transmit(layer->dn, packet);
|
||||
|
||||
|
||||
Linux Driver Implementation
|
||||
CAIF Socket and IP interface
|
||||
===========================
|
||||
|
||||
Linux GPRS Net Device and CAIF socket are implemented on top of the
|
||||
CAIF Core protocol. The Net device and CAIF socket have an instance of
|
||||
The IP interface and CAIF socket API are implemented on top of the
|
||||
CAIF Core protocol. The IP Interface and CAIF socket have an instance of
|
||||
'struct cflayer', just like the CAIF Core protocol stack.
|
||||
Net device and Socket implement the 'receive()' function defined by
|
||||
'struct cflayer', just like the rest of the CAIF stack. In this way, transmit and
|
||||
receive of packets is handled as by the rest of the layers: the 'dn->transmit()'
|
||||
function is called in order to transmit data.
|
||||
|
||||
The layer on top of the CAIF Core implementation is
|
||||
sometimes referred to as the "Client layer".
|
||||
|
||||
|
||||
Configuration of Link Layer
|
||||
---------------------------
|
||||
The Link Layer is implemented as Linux net devices (struct net_device).
|
||||
The Link Layer is implemented as Linux network devices (struct net_device).
|
||||
Payload handling and registration is done using standard Linux mechanisms.
|
||||
|
||||
The CAIF Protocol relies on a loss-less link layer without implementing
|
||||
|
@ -22,7 +22,8 @@ This file contains
|
||||
4.1.2 RAW socket option CAN_RAW_ERR_FILTER
|
||||
4.1.3 RAW socket option CAN_RAW_LOOPBACK
|
||||
4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS
|
||||
4.1.5 RAW socket returned message flags
|
||||
4.1.5 RAW socket option CAN_RAW_FD_FRAMES
|
||||
4.1.6 RAW socket returned message flags
|
||||
4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
|
||||
4.3 connected transport protocols (SOCK_SEQPACKET)
|
||||
4.4 unconnected transport protocols (SOCK_DGRAM)
|
||||
@ -41,7 +42,8 @@ This file contains
|
||||
6.5.1 Netlink interface to set/get devices properties
|
||||
6.5.2 Setting the CAN bit-timing
|
||||
6.5.3 Starting and stopping the CAN network device
|
||||
6.6 supported CAN hardware
|
||||
6.6 CAN FD (flexible data rate) driver support
|
||||
6.7 supported CAN hardware
|
||||
|
||||
7 Socket CAN resources
|
||||
|
||||
@ -232,16 +234,16 @@ solution for a couple of reasons:
|
||||
arbitration problems and error frames caused by the different
|
||||
ECUs. The occurrence of detected errors are important for diagnosis
|
||||
and have to be logged together with the exact timestamp. For this
|
||||
reason the CAN interface driver can generate so called Error Frames
|
||||
that can optionally be passed to the user application in the same
|
||||
way as other CAN frames. Whenever an error on the physical layer
|
||||
reason the CAN interface driver can generate so called Error Message
|
||||
Frames that can optionally be passed to the user application in the
|
||||
same way as other CAN frames. Whenever an error on the physical layer
|
||||
or the MAC layer is detected (e.g. by the CAN controller) the driver
|
||||
creates an appropriate error frame. Error frames can be requested by
|
||||
the user application using the common CAN filter mechanisms. Inside
|
||||
this filter definition the (interested) type of errors may be
|
||||
selected. The reception of error frames is disabled by default.
|
||||
The format of the CAN error frame is briefly described in the Linux
|
||||
header file "include/linux/can/error.h".
|
||||
creates an appropriate error message frame. Error messages frames can
|
||||
be requested by the user application using the common CAN filter
|
||||
mechanisms. Inside this filter definition the (interested) type of
|
||||
errors may be selected. The reception of error messages is disabled
|
||||
by default. The format of the CAN error message frame is briefly
|
||||
described in the Linux header file "include/linux/can/error.h".
|
||||
|
||||
4. How to use Socket CAN
|
||||
------------------------
|
||||
@ -273,7 +275,7 @@ solution for a couple of reasons:
|
||||
|
||||
struct can_frame {
|
||||
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
||||
__u8 can_dlc; /* data length code: 0 .. 8 */
|
||||
__u8 can_dlc; /* frame payload length in byte (0 .. 8) */
|
||||
__u8 data[8] __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
@ -375,6 +377,51 @@ solution for a couple of reasons:
|
||||
nbytes = sendto(s, &frame, sizeof(struct can_frame),
|
||||
0, (struct sockaddr*)&addr, sizeof(addr));
|
||||
|
||||
Remark about CAN FD (flexible data rate) support:
|
||||
|
||||
Generally the handling of CAN FD is very similar to the formerly described
|
||||
examples. The new CAN FD capable CAN controllers support two different
|
||||
bitrates for the arbitration phase and the payload phase of the CAN FD frame
|
||||
and up to 64 bytes of payload. This extended payload length breaks all the
|
||||
kernel interfaces (ABI) which heavily rely on the CAN frame with fixed eight
|
||||
bytes of payload (struct can_frame) like the CAN_RAW socket. Therefore e.g.
|
||||
the CAN_RAW socket supports a new socket option CAN_RAW_FD_FRAMES that
|
||||
switches the socket into a mode that allows the handling of CAN FD frames
|
||||
and (legacy) CAN frames simultaneously (see section 4.1.5).
|
||||
|
||||
The struct canfd_frame is defined in include/linux/can.h:
|
||||
|
||||
struct canfd_frame {
|
||||
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
||||
__u8 len; /* frame payload length in byte (0 .. 64) */
|
||||
__u8 flags; /* additional flags for CAN FD */
|
||||
__u8 __res0; /* reserved / padding */
|
||||
__u8 __res1; /* reserved / padding */
|
||||
__u8 data[64] __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
The struct canfd_frame and the existing struct can_frame have the can_id,
|
||||
the payload length and the payload data at the same offset inside their
|
||||
structures. This allows to handle the different structures very similar.
|
||||
When the content of a struct can_frame is copied into a struct canfd_frame
|
||||
all structure elements can be used as-is - only the data[] becomes extended.
|
||||
|
||||
When introducing the struct canfd_frame it turned out that the data length
|
||||
code (DLC) of the struct can_frame was used as a length information as the
|
||||
length and the DLC has a 1:1 mapping in the range of 0 .. 8. To preserve
|
||||
the easy handling of the length information the canfd_frame.len element
|
||||
contains a plain length value from 0 .. 64. So both canfd_frame.len and
|
||||
can_frame.can_dlc are equal and contain a length information and no DLC.
|
||||
For details about the distinction of CAN and CAN FD capable devices and
|
||||
the mapping to the bus-relevant data length code (DLC), see chapter 6.6.
|
||||
|
||||
The length of the two CAN(FD) frame structures define the maximum transfer
|
||||
unit (MTU) of the CAN(FD) network interface and skbuff data length. Two
|
||||
definitions are specified for CAN specific MTUs in include/linux/can.h :
|
||||
|
||||
#define CAN_MTU (sizeof(struct can_frame)) == 16 => 'legacy' CAN frame
|
||||
#define CANFD_MTU (sizeof(struct canfd_frame)) == 72 => CAN FD frame
|
||||
|
||||
4.1 RAW protocol sockets with can_filters (SOCK_RAW)
|
||||
|
||||
Using CAN_RAW sockets is extensively comparable to the commonly
|
||||
@ -383,7 +430,7 @@ solution for a couple of reasons:
|
||||
defaults are set at RAW socket binding time:
|
||||
|
||||
- The filters are set to exactly one filter receiving everything
|
||||
- The socket only receives valid data frames (=> no error frames)
|
||||
- The socket only receives valid data frames (=> no error message frames)
|
||||
- The loopback of sent CAN frames is enabled (see chapter 3.2)
|
||||
- The socket does not receive its own sent frames (in loopback mode)
|
||||
|
||||
@ -434,7 +481,7 @@ solution for a couple of reasons:
|
||||
4.1.2 RAW socket option CAN_RAW_ERR_FILTER
|
||||
|
||||
As described in chapter 3.4 the CAN interface driver can generate so
|
||||
called Error Frames that can optionally be passed to the user
|
||||
called Error Message Frames that can optionally be passed to the user
|
||||
application in the same way as other CAN frames. The possible
|
||||
errors are divided into different error classes that may be filtered
|
||||
using the appropriate error mask. To register for every possible
|
||||
@ -472,7 +519,69 @@ solution for a couple of reasons:
|
||||
setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
|
||||
&recv_own_msgs, sizeof(recv_own_msgs));
|
||||
|
||||
4.1.5 RAW socket returned message flags
|
||||
4.1.5 RAW socket option CAN_RAW_FD_FRAMES
|
||||
|
||||
CAN FD support in CAN_RAW sockets can be enabled with a new socket option
|
||||
CAN_RAW_FD_FRAMES which is off by default. When the new socket option is
|
||||
not supported by the CAN_RAW socket (e.g. on older kernels), switching the
|
||||
CAN_RAW_FD_FRAMES option returns the error -ENOPROTOOPT.
|
||||
|
||||
Once CAN_RAW_FD_FRAMES is enabled the application can send both CAN frames
|
||||
and CAN FD frames. OTOH the application has to handle CAN and CAN FD frames
|
||||
when reading from the socket.
|
||||
|
||||
CAN_RAW_FD_FRAMES enabled: CAN_MTU and CANFD_MTU are allowed
|
||||
CAN_RAW_FD_FRAMES disabled: only CAN_MTU is allowed (default)
|
||||
|
||||
Example:
|
||||
[ remember: CANFD_MTU == sizeof(struct canfd_frame) ]
|
||||
|
||||
struct canfd_frame cfd;
|
||||
|
||||
nbytes = read(s, &cfd, CANFD_MTU);
|
||||
|
||||
if (nbytes == CANFD_MTU) {
|
||||
printf("got CAN FD frame with length %d\n", cfd.len);
|
||||
/* cfd.flags contains valid data */
|
||||
} else if (nbytes == CAN_MTU) {
|
||||
printf("got legacy CAN frame with length %d\n", cfd.len);
|
||||
/* cfd.flags is undefined */
|
||||
} else {
|
||||
fprintf(stderr, "read: invalid CAN(FD) frame\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the content can be handled independently from the received MTU size */
|
||||
|
||||
printf("can_id: %X data length: %d data: ", cfd.can_id, cfd.len);
|
||||
for (i = 0; i < cfd.len; i++)
|
||||
printf("%02X ", cfd.data[i]);
|
||||
|
||||
When reading with size CANFD_MTU only returns CAN_MTU bytes that have
|
||||
been received from the socket a legacy CAN frame has been read into the
|
||||
provided CAN FD structure. Note that the canfd_frame.flags data field is
|
||||
not specified in the struct can_frame and therefore it is only valid in
|
||||
CANFD_MTU sized CAN FD frames.
|
||||
|
||||
As long as the payload length is <=8 the received CAN frames from CAN FD
|
||||
capable CAN devices can be received and read by legacy sockets too. When
|
||||
user-generated CAN FD frames have a payload length <=8 these can be send
|
||||
by legacy CAN network interfaces too. Sending CAN FD frames with payload
|
||||
length > 8 to a legacy CAN network interface returns an -EMSGSIZE error.
|
||||
|
||||
Implementation hint for new CAN applications:
|
||||
|
||||
To build a CAN FD aware application use struct canfd_frame as basic CAN
|
||||
data structure for CAN_RAW based applications. When the application is
|
||||
executed on an older Linux kernel and switching the CAN_RAW_FD_FRAMES
|
||||
socket option returns an error: No problem. You'll get legacy CAN frames
|
||||
or CAN FD frames and can process them the same way.
|
||||
|
||||
When sending to CAN devices make sure that the device is capable to handle
|
||||
CAN FD frames by checking if the device maximum transfer unit is CANFD_MTU.
|
||||
The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall.
|
||||
|
||||
4.1.6 RAW socket returned message flags
|
||||
|
||||
When using recvmsg() call, the msg->msg_flags may contain following flags:
|
||||
|
||||
@ -527,7 +636,7 @@ solution for a couple of reasons:
|
||||
|
||||
rcvlist_all - list for unfiltered entries (no filter operations)
|
||||
rcvlist_eff - list for single extended frame (EFF) entries
|
||||
rcvlist_err - list for error frames masks
|
||||
rcvlist_err - list for error message frames masks
|
||||
rcvlist_fil - list for mask/value filters
|
||||
rcvlist_inv - list for mask/value filters (inverse semantic)
|
||||
rcvlist_sff - list for single standard frame (SFF) entries
|
||||
@ -573,10 +682,13 @@ solution for a couple of reasons:
|
||||
dev->type = ARPHRD_CAN; /* the netdevice hardware type */
|
||||
dev->flags = IFF_NOARP; /* CAN has no arp */
|
||||
|
||||
dev->mtu = sizeof(struct can_frame);
|
||||
dev->mtu = CAN_MTU; /* sizeof(struct can_frame) -> legacy CAN interface */
|
||||
|
||||
The struct can_frame is the payload of each socket buffer in the
|
||||
protocol family PF_CAN.
|
||||
or alternative, when the controller supports CAN with flexible data rate:
|
||||
dev->mtu = CANFD_MTU; /* sizeof(struct canfd_frame) -> CAN FD interface */
|
||||
|
||||
The struct can_frame or struct canfd_frame is the payload of each socket
|
||||
buffer (skbuff) in the protocol family PF_CAN.
|
||||
|
||||
6.2 local loopback of sent frames
|
||||
|
||||
@ -784,15 +896,41 @@ solution for a couple of reasons:
|
||||
$ ip link set canX type can restart-ms 100
|
||||
|
||||
Alternatively, the application may realize the "bus-off" condition
|
||||
by monitoring CAN error frames and do a restart when appropriate with
|
||||
the command:
|
||||
by monitoring CAN error message frames and do a restart when
|
||||
appropriate with the command:
|
||||
|
||||
$ ip link set canX type can restart
|
||||
|
||||
Note that a restart will also create a CAN error frame (see also
|
||||
chapter 3.4).
|
||||
Note that a restart will also create a CAN error message frame (see
|
||||
also chapter 3.4).
|
||||
|
||||
6.6 Supported CAN hardware
|
||||
6.6 CAN FD (flexible data rate) driver support
|
||||
|
||||
CAN FD capable CAN controllers support two different bitrates for the
|
||||
arbitration phase and the payload phase of the CAN FD frame. Therefore a
|
||||
second bittiming has to be specified in order to enable the CAN FD bitrate.
|
||||
|
||||
Additionally CAN FD capable CAN controllers support up to 64 bytes of
|
||||
payload. The representation of this length in can_frame.can_dlc and
|
||||
canfd_frame.len for userspace applications and inside the Linux network
|
||||
layer is a plain value from 0 .. 64 instead of the CAN 'data length code'.
|
||||
The data length code was a 1:1 mapping to the payload length in the legacy
|
||||
CAN frames anyway. The payload length to the bus-relevant DLC mapping is
|
||||
only performed inside the CAN drivers, preferably with the helper
|
||||
functions can_dlc2len() and can_len2dlc().
|
||||
|
||||
The CAN netdevice driver capabilities can be distinguished by the network
|
||||
devices maximum transfer unit (MTU):
|
||||
|
||||
MTU = 16 (CAN_MTU) => sizeof(struct can_frame) => 'legacy' CAN device
|
||||
MTU = 72 (CANFD_MTU) => sizeof(struct canfd_frame) => CAN FD capable device
|
||||
|
||||
The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall.
|
||||
N.B. CAN FD capable devices can also handle and send legacy CAN frames.
|
||||
|
||||
FIXME: Add details about the CAN FD controller configuration when available.
|
||||
|
||||
6.7 Supported CAN hardware
|
||||
|
||||
Please check the "Kconfig" file in "drivers/net/can" to get an actual
|
||||
list of the support CAN hardware. On the Socket CAN project website
|
||||
|
@ -468,6 +468,19 @@ tcp_syncookies - BOOLEAN
|
||||
SYN flood warnings in logs not being really flooded, your server
|
||||
is seriously misconfigured.
|
||||
|
||||
tcp_fastopen - INTEGER
|
||||
Enable TCP Fast Open feature (draft-ietf-tcpm-fastopen) to send data
|
||||
in the opening SYN packet. To use this feature, the client application
|
||||
must not use connect(). Instead, it should use sendmsg() or sendto()
|
||||
with MSG_FASTOPEN flag which performs a TCP handshake automatically.
|
||||
|
||||
The values (bitmap) are:
|
||||
1: Enables sending data in the opening SYN on the client
|
||||
5: Enables sending data in the opening SYN on the client regardless
|
||||
of cookie availability.
|
||||
|
||||
Default: 0
|
||||
|
||||
tcp_syn_retries - INTEGER
|
||||
Number of times initial SYNs for an active TCP connection attempt
|
||||
will be retransmitted. Should not be higher than 255. Default value
|
||||
@ -551,6 +564,25 @@ tcp_thin_dupack - BOOLEAN
|
||||
Documentation/networking/tcp-thin.txt
|
||||
Default: 0
|
||||
|
||||
tcp_limit_output_bytes - INTEGER
|
||||
Controls TCP Small Queue limit per tcp socket.
|
||||
TCP bulk sender tends to increase packets in flight until it
|
||||
gets losses notifications. With SNDBUF autotuning, this can
|
||||
result in a large amount of packets queued in qdisc/device
|
||||
on the local machine, hurting latency of other flows, for
|
||||
typical pfifo_fast qdiscs.
|
||||
tcp_limit_output_bytes limits the number of bytes on qdisc
|
||||
or device to reduce artificial RTT/cwnd and reduce bufferbloat.
|
||||
Note: For GSO/TSO enabled flows, we try to have at least two
|
||||
packets in flight. Reducing tcp_limit_output_bytes might also
|
||||
reduce the size of individual GSO packet (64KB being the max)
|
||||
Default: 131072
|
||||
|
||||
tcp_challenge_ack_limit - INTEGER
|
||||
Limits number of Challenge ACK sent per second, as recommended
|
||||
in RFC 5961 (Improving TCP's Robustness to Blind In-Window Attacks)
|
||||
Default: 100
|
||||
|
||||
UDP variables:
|
||||
|
||||
udp_mem - vector of 3 INTEGERs: min, pressure, max
|
||||
@ -857,9 +889,19 @@ accept_source_route - BOOLEAN
|
||||
FALSE (host)
|
||||
|
||||
accept_local - BOOLEAN
|
||||
Accept packets with local source addresses. In combination with
|
||||
suitable routing, this can be used to direct packets between two
|
||||
local interfaces over the wire and have them accepted properly.
|
||||
Accept packets with local source addresses. In combination
|
||||
with suitable routing, this can be used to direct packets
|
||||
between two local interfaces over the wire and have them
|
||||
accepted properly.
|
||||
|
||||
rp_filter must be set to a non-zero value in order for
|
||||
accept_local to have an effect.
|
||||
|
||||
default FALSE
|
||||
|
||||
route_localnet - BOOLEAN
|
||||
Do not consider loopback addresses as martian source or destination
|
||||
while routing. This enables the use of 127/8 for local routing purposes.
|
||||
default FALSE
|
||||
|
||||
rp_filter - INTEGER
|
||||
@ -1398,6 +1440,20 @@ path_max_retrans - INTEGER
|
||||
|
||||
Default: 5
|
||||
|
||||
pf_retrans - INTEGER
|
||||
The number of retransmissions that will be attempted on a given path
|
||||
before traffic is redirected to an alternate transport (should one
|
||||
exist). Note this is distinct from path_max_retrans, as a path that
|
||||
passes the pf_retrans threshold can still be used. Its only
|
||||
deprioritized when a transmission path is selected by the stack. This
|
||||
setting is primarily used to enable fast failover mechanisms without
|
||||
having to reduce path_max_retrans to a very low value. See:
|
||||
http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
|
||||
for details. Note also that a value of pf_retrans > path_max_retrans
|
||||
disables this feature
|
||||
|
||||
Default: 0
|
||||
|
||||
rto_initial - INTEGER
|
||||
The initial round trip timeout value in milliseconds that will be used
|
||||
in calculating round trip times. This is the initial time interval
|
||||
|
@ -118,7 +118,7 @@ essentially like this, ignoring metadata:
|
||||
Naively, to add VLAN support, it makes sense to add a new "vlan" flow
|
||||
key attribute to contain the VLAN tag, then continue to decode the
|
||||
encapsulated headers beyond the VLAN tag using the existing field
|
||||
definitions. With this change, an TCP packet in VLAN 10 would have a
|
||||
definitions. With this change, a TCP packet in VLAN 10 would have a
|
||||
flow key much like this:
|
||||
|
||||
eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
|
||||
|
@ -136,16 +136,6 @@ For more information, please review the AMD8131 errata at
|
||||
http://vip.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/
|
||||
26310_AMD-8131_HyperTransport_PCI-X_Tunnel_Revision_Guide_rev_3_18.pdf
|
||||
|
||||
6. Available Downloads
|
||||
Neterion "s2io" driver in Red Hat and Suse 2.6-based distributions is kept up
|
||||
to date, also the latest "s2io" code (including support for 2.4 kernels) is
|
||||
available via "Support" link on the Neterion site: http://www.neterion.com.
|
||||
|
||||
For Xframe User Guide (Programming manual), visit ftp site ns1.s2io.com,
|
||||
user: linuxdocs password: HALdocs
|
||||
|
||||
7. Support
|
||||
6. Support
|
||||
For further support please contact either your 10GbE Xframe NIC vendor (IBM,
|
||||
HP, SGI etc.) or click on the "Support" link on the Neterion site:
|
||||
http://www.neterion.com.
|
||||
|
||||
HP, SGI etc.)
|
||||
|
@ -257,9 +257,11 @@ reset procedure etc).
|
||||
o Makefile
|
||||
o stmmac_main.c: main network device driver;
|
||||
o stmmac_mdio.c: mdio functions;
|
||||
o stmmac_pci: PCI driver;
|
||||
o stmmac_platform.c: platform driver
|
||||
o stmmac_ethtool.c: ethtool support;
|
||||
o stmmac_timer.[ch]: timer code used for mitigating the driver dma interrupts
|
||||
Only tested on ST40 platforms based.
|
||||
(only tested on ST40 platforms based);
|
||||
o stmmac.h: private driver structure;
|
||||
o common.h: common definitions and VFTs;
|
||||
o descs.h: descriptor structure definitions;
|
||||
@ -269,9 +271,11 @@ reset procedure etc).
|
||||
o dwmac100_core: MAC 100 core and dma code;
|
||||
o dwmac100_dma.c: dma funtions for the MAC chip;
|
||||
o dwmac1000.h: specific header file for the MAC;
|
||||
o dwmac_lib.c: generic DMA functions shared among chips
|
||||
o enh_desc.c: functions for handling enhanced descriptors
|
||||
o norm_desc.c: functions for handling normal descriptors
|
||||
o dwmac_lib.c: generic DMA functions shared among chips;
|
||||
o enh_desc.c: functions for handling enhanced descriptors;
|
||||
o norm_desc.c: functions for handling normal descriptors;
|
||||
o chain_mode.c/ring_mode.c:: functions to manage RING/CHAINED modes;
|
||||
o mmc_core.c/mmc.h: Management MAC Counters;
|
||||
|
||||
5) Debug Information
|
||||
|
||||
@ -304,7 +308,27 @@ All these are only useful during the developing stage
|
||||
and should never enabled inside the code for general usage.
|
||||
In fact, these can generate an huge amount of debug messages.
|
||||
|
||||
6) TODO:
|
||||
6) Energy Efficient Ethernet
|
||||
|
||||
Energy Efficient Ethernet(EEE) enables IEEE 802.3 MAC sublayer along
|
||||
with a family of Physical layer to operate in the Low power Idle(LPI)
|
||||
mode. The EEE mode supports the IEEE 802.3 MAC operation at 100Mbps,
|
||||
1000Mbps & 10Gbps.
|
||||
|
||||
The LPI mode allows power saving by switching off parts of the
|
||||
communication device functionality when there is no data to be
|
||||
transmitted & received. The system on both the side of the link can
|
||||
disable some functionalities & save power during the period of low-link
|
||||
utilization. The MAC controls whether the system should enter or exit
|
||||
the LPI mode & communicate this to PHY.
|
||||
|
||||
As soon as the interface is opened, the driver verifies if the EEE can
|
||||
be supported. This is done by looking at both the DMA HW capability
|
||||
register and the PHY devices MCD registers.
|
||||
To enter in Tx LPI mode the driver needs to have a software timer
|
||||
that enable and disable the LPI mode when there is nothing to be
|
||||
transmitted.
|
||||
|
||||
7) TODO:
|
||||
o XGMAC is not supported.
|
||||
o Add the EEE - Energy Efficient Ethernet
|
||||
o Add the PTP - precision time protocol
|
||||
|
@ -91,10 +91,3 @@ v) addr_learn_en
|
||||
virtualization environment.
|
||||
Valid range: 0,1 (disabled, enabled respectively)
|
||||
Default: 0
|
||||
|
||||
4) Troubleshooting:
|
||||
-------------------
|
||||
|
||||
To resolve an issue with the source code or X3100 series adapter, please collect
|
||||
the statistics, register dumps using ethool, relevant logs and email them to
|
||||
support@neterion.com.
|
||||
|
@ -178,3 +178,36 @@ ANY_GET_PARAMETER to the reader A gate to get information on the target
|
||||
that was discovered).
|
||||
|
||||
Typically, such an event will be propagated to NFC Core from MSGRXWQ context.
|
||||
|
||||
Error management
|
||||
----------------
|
||||
|
||||
Errors that occur synchronously with the execution of an NFC Core request are
|
||||
simply returned as the execution result of the request. These are easy.
|
||||
|
||||
Errors that occur asynchronously (e.g. in a background protocol handling thread)
|
||||
must be reported such that upper layers don't stay ignorant that something
|
||||
went wrong below and know that expected events will probably never happen.
|
||||
Handling of these errors is done as follows:
|
||||
|
||||
- driver (pn544) fails to deliver an incoming frame: it stores the error such
|
||||
that any subsequent call to the driver will result in this error. Then it calls
|
||||
the standard nfc_shdlc_recv_frame() with a NULL argument to report the problem
|
||||
above. shdlc stores a EREMOTEIO sticky status, which will trigger SMW to
|
||||
report above in turn.
|
||||
|
||||
- SMW is basically a background thread to handle incoming and outgoing shdlc
|
||||
frames. This thread will also check the shdlc sticky status and report to HCI
|
||||
when it discovers it is not able to run anymore because of an unrecoverable
|
||||
error that happened within shdlc or below. If the problem occurs during shdlc
|
||||
connection, the error is reported through the connect completion.
|
||||
|
||||
- HCI: if an internal HCI error happens (frame is lost), or HCI is reported an
|
||||
error from a lower layer, HCI will either complete the currently executing
|
||||
command with that error, or notify NFC Core directly if no command is executing.
|
||||
|
||||
- NFC Core: when NFC Core is notified of an error from below and polling is
|
||||
active, it will send a tag discovered event with an empty tag list to the user
|
||||
space to let it know that the poll operation will never be able to detect a tag.
|
||||
If polling is not active and the error was sticky, lower levels will return it
|
||||
at next invocation.
|
||||
|
30
MAINTAINERS
30
MAINTAINERS
@ -329,7 +329,7 @@ F: drivers/hwmon/adm1029.c
|
||||
|
||||
ADM8211 WIRELESS DRIVER
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://linuxwireless.org/
|
||||
W: http://wireless.kernel.org/
|
||||
S: Orphan
|
||||
F: drivers/net/wireless/adm8211.*
|
||||
|
||||
@ -1441,7 +1441,7 @@ B43 WIRELESS DRIVER
|
||||
M: Stefano Brivio <stefano.brivio@polimi.it>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
L: b43-dev@lists.infradead.org
|
||||
W: http://linuxwireless.org/en/users/Drivers/b43
|
||||
W: http://wireless.kernel.org/en/users/Drivers/b43
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/b43/
|
||||
|
||||
@ -1450,7 +1450,7 @@ M: Larry Finger <Larry.Finger@lwfinger.net>
|
||||
M: Stefano Brivio <stefano.brivio@polimi.it>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
L: b43-dev@lists.infradead.org
|
||||
W: http://linuxwireless.org/en/users/Drivers/b43
|
||||
W: http://wireless.kernel.org/en/users/Drivers/b43
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/b43legacy/
|
||||
|
||||
@ -1613,6 +1613,7 @@ M: Arend van Spriel <arend@broadcom.com>
|
||||
M: Franky (Zhenhui) Lin <frankyl@broadcom.com>
|
||||
M: Kan Yan <kanyan@broadcom.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
L: brcm80211-dev-list@broadcom.com
|
||||
S: Supported
|
||||
F: drivers/net/wireless/brcm80211/
|
||||
|
||||
@ -3679,14 +3680,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
|
||||
S: Supported
|
||||
F: drivers/net/wireless/iwlwifi/
|
||||
|
||||
INTEL WIRELESS MULTICOMM 3200 WIFI (iwmc3200wifi)
|
||||
M: Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
M: Intel Linux Wireless <ilw@linux.intel.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://wireless.kernel.org/en/users/Drivers/iwmc3200wifi
|
||||
F: drivers/net/wireless/iwmc3200wifi/
|
||||
|
||||
INTEL MANAGEMENT ENGINE (mei)
|
||||
M: Tomas Winkler <tomas.winkler@intel.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
@ -4370,7 +4363,7 @@ F: arch/m68k/hp300/
|
||||
MAC80211
|
||||
M: Johannes Berg <johannes@sipsolutions.net>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://linuxwireless.org/
|
||||
W: http://wireless.kernel.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
|
||||
S: Maintained
|
||||
@ -4382,7 +4375,7 @@ MAC80211 PID RATE CONTROL
|
||||
M: Stefano Brivio <stefano.brivio@polimi.it>
|
||||
M: Mattias Nissler <mattias.nissler@gmx.de>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://linuxwireless.org/en/developers/Documentation/mac80211/RateControl/PID
|
||||
W: http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
|
||||
S: Maintained
|
||||
@ -4611,7 +4604,6 @@ S: Maintained
|
||||
F: drivers/usb/musb/
|
||||
|
||||
MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
|
||||
M: Jon Mason <mason@myri.com>
|
||||
M: Andrew Gallatin <gallatin@myri.com>
|
||||
L: netdev@vger.kernel.org
|
||||
W: http://www.myri.com/scs/download-Myri10GE.html
|
||||
@ -4656,8 +4648,6 @@ F: net/sched/sch_netem.c
|
||||
NETERION 10GbE DRIVERS (s2io/vxge)
|
||||
M: Jon Mason <jdmason@kudzu.us>
|
||||
L: netdev@vger.kernel.org
|
||||
W: http://trac.neterion.com/cgi-bin/trac.cgi/wiki/Linux?Anonymous
|
||||
W: http://trac.neterion.com/cgi-bin/trac.cgi/wiki/X3100Linux?Anonymous
|
||||
S: Supported
|
||||
F: Documentation/networking/s2io.txt
|
||||
F: Documentation/networking/vxge.txt
|
||||
@ -5068,7 +5058,7 @@ F: fs/ocfs2/
|
||||
|
||||
ORINOCO DRIVER
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://linuxwireless.org/en/users/Drivers/orinoco
|
||||
W: http://wireless.kernel.org/en/users/Drivers/orinoco
|
||||
W: http://www.nongnu.org/orinoco/
|
||||
S: Orphan
|
||||
F: drivers/net/wireless/orinoco/
|
||||
@ -5772,7 +5762,7 @@ F: net/rose/
|
||||
RTL8180 WIRELESS DRIVER
|
||||
M: "John W. Linville" <linville@tuxdriver.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://linuxwireless.org/
|
||||
W: http://wireless.kernel.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/rtl818x/rtl8180/
|
||||
@ -5782,7 +5772,7 @@ M: Herton Ronaldo Krzesinski <herton@canonical.com>
|
||||
M: Hin-Tak Leung <htl10@users.sourceforge.net>
|
||||
M: Larry Finger <Larry.Finger@lwfinger.net>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://linuxwireless.org/
|
||||
W: http://wireless.kernel.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/rtl818x/rtl8187/
|
||||
@ -5791,7 +5781,7 @@ RTL8192CE WIRELESS DRIVER
|
||||
M: Larry Finger <Larry.Finger@lwfinger.net>
|
||||
M: Chaoming Li <chaoming_li@realsil.com.cn>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://linuxwireless.org/
|
||||
W: http://wireless.kernel.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/rtlwifi/
|
||||
|
@ -873,7 +873,7 @@ static struct adf702x_platform_data adf7021_platform_data = {
|
||||
};
|
||||
static inline void adf702x_mac_init(void)
|
||||
{
|
||||
random_ether_addr(adf7021_platform_data.mac_addr);
|
||||
eth_random_addr(adf7021_platform_data.mac_addr);
|
||||
}
|
||||
#else
|
||||
static inline void adf702x_mac_init(void) {}
|
||||
|
@ -80,7 +80,7 @@ int soc_mac_addr(unsigned int index, u8 *addr)
|
||||
if (have_fuse_mac)
|
||||
memcpy(addr, c6x_fuse_mac, 6);
|
||||
else
|
||||
random_ether_addr(addr);
|
||||
eth_random_addr(addr);
|
||||
}
|
||||
|
||||
/* adjust for specific EMAC device */
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* mcfne.h -- NE2000 in ColdFire eval boards.
|
||||
* mcf8390.h -- NS8390 support for ColdFire eval boards.
|
||||
*
|
||||
* (C) Copyright 1999-2000, Greg Ungerer (gerg@snapgear.com)
|
||||
* (C) Copyright 2000, Lineo (www.lineo.com)
|
||||
@ -14,8 +14,8 @@
|
||||
*/
|
||||
|
||||
/****************************************************************************/
|
||||
#ifndef mcfne_h
|
||||
#define mcfne_h
|
||||
#ifndef mcf8390_h
|
||||
#define mcf8390_h
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
#if defined(CONFIG_ARN5206)
|
||||
#define NE2000_ADDR 0x40000300
|
||||
#define NE2000_ODDOFFSET 0x00010000
|
||||
#define NE2000_ADDRSIZE 0x00020000
|
||||
#define NE2000_IRQ_VECTOR 0xf0
|
||||
#define NE2000_IRQ_PRIORITY 2
|
||||
#define NE2000_IRQ_LEVEL 4
|
||||
@ -46,6 +47,7 @@
|
||||
#if defined(CONFIG_M5206eC3)
|
||||
#define NE2000_ADDR 0x40000300
|
||||
#define NE2000_ODDOFFSET 0x00010000
|
||||
#define NE2000_ADDRSIZE 0x00020000
|
||||
#define NE2000_IRQ_VECTOR 0x1c
|
||||
#define NE2000_IRQ_PRIORITY 2
|
||||
#define NE2000_IRQ_LEVEL 4
|
||||
@ -54,6 +56,7 @@
|
||||
|
||||
#if defined(CONFIG_M5206e) && defined(CONFIG_NETtel)
|
||||
#define NE2000_ADDR 0x30000300
|
||||
#define NE2000_ADDRSIZE 0x00001000
|
||||
#define NE2000_IRQ_VECTOR 25
|
||||
#define NE2000_IRQ_PRIORITY 1
|
||||
#define NE2000_IRQ_LEVEL 3
|
||||
@ -63,6 +66,7 @@
|
||||
#if defined(CONFIG_M5307C3)
|
||||
#define NE2000_ADDR 0x40000300
|
||||
#define NE2000_ODDOFFSET 0x00010000
|
||||
#define NE2000_ADDRSIZE 0x00020000
|
||||
#define NE2000_IRQ_VECTOR 0x1b
|
||||
#define NE2000_BYTE volatile unsigned short
|
||||
#endif
|
||||
@ -70,6 +74,7 @@
|
||||
#if defined(CONFIG_M5272) && defined(CONFIG_NETtel)
|
||||
#define NE2000_ADDR 0x30600300
|
||||
#define NE2000_ODDOFFSET 0x00008000
|
||||
#define NE2000_ADDRSIZE 0x00010000
|
||||
#define NE2000_IRQ_VECTOR 67
|
||||
#undef BSWAP
|
||||
#define BSWAP(w) (w)
|
||||
@ -82,6 +87,7 @@
|
||||
#define NE2000_ADDR0 0x30600300
|
||||
#define NE2000_ADDR1 0x30800300
|
||||
#define NE2000_ODDOFFSET 0x00008000
|
||||
#define NE2000_ADDRSIZE 0x00010000
|
||||
#define NE2000_IRQ_VECTOR0 27
|
||||
#define NE2000_IRQ_VECTOR1 29
|
||||
#undef BSWAP
|
||||
@ -94,6 +100,7 @@
|
||||
#if defined(CONFIG_M5307) && defined(CONFIG_SECUREEDGEMP3)
|
||||
#define NE2000_ADDR 0x30600300
|
||||
#define NE2000_ODDOFFSET 0x00008000
|
||||
#define NE2000_ADDRSIZE 0x00010000
|
||||
#define NE2000_IRQ_VECTOR 27
|
||||
#undef BSWAP
|
||||
#define BSWAP(w) (w)
|
||||
@ -105,6 +112,7 @@
|
||||
#if defined(CONFIG_ARN5307)
|
||||
#define NE2000_ADDR 0xfe600300
|
||||
#define NE2000_ODDOFFSET 0x00010000
|
||||
#define NE2000_ADDRSIZE 0x00020000
|
||||
#define NE2000_IRQ_VECTOR 0x1b
|
||||
#define NE2000_IRQ_PRIORITY 2
|
||||
#define NE2000_IRQ_LEVEL 3
|
||||
@ -114,129 +122,10 @@
|
||||
#if defined(CONFIG_M5407C3)
|
||||
#define NE2000_ADDR 0x40000300
|
||||
#define NE2000_ODDOFFSET 0x00010000
|
||||
#define NE2000_ADDRSIZE 0x00020000
|
||||
#define NE2000_IRQ_VECTOR 0x1b
|
||||
#define NE2000_BYTE volatile unsigned short
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* Side-band address space for odd address requires re-mapping
|
||||
* many of the standard ISA access functions.
|
||||
*/
|
||||
#ifdef NE2000_ODDOFFSET
|
||||
|
||||
#undef outb
|
||||
#undef outb_p
|
||||
#undef inb
|
||||
#undef inb_p
|
||||
#undef outsb
|
||||
#undef outsw
|
||||
#undef insb
|
||||
#undef insw
|
||||
|
||||
#define outb ne2000_outb
|
||||
#define inb ne2000_inb
|
||||
#define outb_p ne2000_outb
|
||||
#define inb_p ne2000_inb
|
||||
#define outsb ne2000_outsb
|
||||
#define outsw ne2000_outsw
|
||||
#define insb ne2000_insb
|
||||
#define insw ne2000_insw
|
||||
|
||||
|
||||
#ifndef COLDFIRE_NE2000_FUNCS
|
||||
|
||||
void ne2000_outb(unsigned int val, unsigned int addr);
|
||||
int ne2000_inb(unsigned int addr);
|
||||
void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len);
|
||||
void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len);
|
||||
void ne2000_outsb(unsigned int addr, void *vbuf, unsigned long len);
|
||||
void ne2000_outsw(unsigned int addr, void *vbuf, unsigned long len);
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* This macro converts a conventional register address into the
|
||||
* real memory pointer of the mapped NE2000 device.
|
||||
* On most NE2000 implementations on ColdFire boards the chip is
|
||||
* mapped in kinda funny, due to its ISA heritage.
|
||||
*/
|
||||
#define NE2000_PTR(addr) ((addr&0x1)?(NE2000_ODDOFFSET+addr-1):(addr))
|
||||
#define NE2000_DATA_PTR(addr) (addr)
|
||||
|
||||
|
||||
void ne2000_outb(unsigned int val, unsigned int addr)
|
||||
{
|
||||
NE2000_BYTE *rp;
|
||||
|
||||
rp = (NE2000_BYTE *) NE2000_PTR(addr);
|
||||
*rp = RSWAP(val);
|
||||
}
|
||||
|
||||
int ne2000_inb(unsigned int addr)
|
||||
{
|
||||
NE2000_BYTE *rp, val;
|
||||
|
||||
rp = (NE2000_BYTE *) NE2000_PTR(addr);
|
||||
val = *rp;
|
||||
return((int) ((NE2000_BYTE) RSWAP(val)));
|
||||
}
|
||||
|
||||
void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len)
|
||||
{
|
||||
NE2000_BYTE *rp, val;
|
||||
unsigned char *buf;
|
||||
|
||||
buf = (unsigned char *) vbuf;
|
||||
rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
|
||||
for (; (len > 0); len--) {
|
||||
val = *rp;
|
||||
*buf++ = RSWAP(val);
|
||||
}
|
||||
}
|
||||
|
||||
void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len)
|
||||
{
|
||||
volatile unsigned short *rp;
|
||||
unsigned short w, *buf;
|
||||
|
||||
buf = (unsigned short *) vbuf;
|
||||
rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
|
||||
for (; (len > 0); len--) {
|
||||
w = *rp;
|
||||
*buf++ = BSWAP(w);
|
||||
}
|
||||
}
|
||||
|
||||
void ne2000_outsb(unsigned int addr, const void *vbuf, unsigned long len)
|
||||
{
|
||||
NE2000_BYTE *rp, val;
|
||||
unsigned char *buf;
|
||||
|
||||
buf = (unsigned char *) vbuf;
|
||||
rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
|
||||
for (; (len > 0); len--) {
|
||||
val = *buf++;
|
||||
*rp = RSWAP(val);
|
||||
}
|
||||
}
|
||||
|
||||
void ne2000_outsw(unsigned int addr, const void *vbuf, unsigned long len)
|
||||
{
|
||||
volatile unsigned short *rp;
|
||||
unsigned short w, *buf;
|
||||
|
||||
buf = (unsigned short *) vbuf;
|
||||
rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
|
||||
for (; (len > 0); len--) {
|
||||
w = *buf++;
|
||||
*rp = BSWAP(w);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* COLDFIRE_NE2000_FUNCS */
|
||||
#endif /* NE2000_OFFOFFSET */
|
||||
|
||||
/****************************************************************************/
|
||||
#endif /* mcfne_h */
|
||||
#endif /* mcf8390_h */
|
@ -310,10 +310,10 @@ static void __init cpmac_get_mac(int instance, unsigned char *dev_addr)
|
||||
&dev_addr[4], &dev_addr[5]) != 6) {
|
||||
pr_warning("cannot parse mac address, "
|
||||
"using random address\n");
|
||||
random_ether_addr(dev_addr);
|
||||
eth_random_addr(dev_addr);
|
||||
}
|
||||
} else
|
||||
random_ether_addr(dev_addr);
|
||||
eth_random_addr(dev_addr);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -254,7 +254,7 @@ early_param("rfmac", rfmac_param);
|
||||
* Generates an Ethernet MAC address that is highly likely to be unique for
|
||||
* this particular system on a network with other systems of the same type.
|
||||
*
|
||||
* The problem we are solving is that, when random_ether_addr() is used to
|
||||
* The problem we are solving is that, when eth_random_addr() is used to
|
||||
* generate MAC addresses at startup, there isn't much entropy for the random
|
||||
* number generator to use and the addresses it produces are fairly likely to
|
||||
* be the same as those of other identical systems on the same local network.
|
||||
@ -269,7 +269,7 @@ early_param("rfmac", rfmac_param);
|
||||
* Still, this does give us something to work with.
|
||||
*
|
||||
* The approach we take is:
|
||||
* 1. If we can't get the RF MAC Address, just call random_ether_addr.
|
||||
* 1. If we can't get the RF MAC Address, just call eth_random_addr.
|
||||
* 2. Use the 24-bit NIC-specific bits of the RF MAC address as the last 24
|
||||
* bits of the new address. This is very likely to be unique, except for
|
||||
* the current box.
|
||||
@ -299,7 +299,7 @@ void platform_random_ether_addr(u8 addr[ETH_ALEN])
|
||||
if (!have_rfmac) {
|
||||
pr_warning("rfmac not available on command line; "
|
||||
"generating random MAC address\n");
|
||||
random_ether_addr(addr);
|
||||
eth_random_addr(addr);
|
||||
}
|
||||
|
||||
else {
|
||||
|
@ -96,6 +96,7 @@ static void bpf_flush_icache(void *start_, void *end_)
|
||||
#define AND F3(2, 0x01)
|
||||
#define ANDCC F3(2, 0x11)
|
||||
#define OR F3(2, 0x02)
|
||||
#define XOR F3(2, 0x03)
|
||||
#define SUB F3(2, 0x04)
|
||||
#define SUBCC F3(2, 0x14)
|
||||
#define MUL F3(2, 0x0a) /* umul */
|
||||
@ -462,6 +463,9 @@ void bpf_jit_compile(struct sk_filter *fp)
|
||||
case BPF_S_ALU_OR_K: /* A |= K */
|
||||
emit_alu_K(OR, K);
|
||||
break;
|
||||
case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
|
||||
emit_alu_X(XOR);
|
||||
break;
|
||||
case BPF_S_ALU_LSH_X: /* A <<= X */
|
||||
emit_alu_X(SLL);
|
||||
break;
|
||||
|
@ -339,7 +339,7 @@ static int setup_etheraddr(char *str, unsigned char *addr, char *name)
|
||||
random:
|
||||
printk(KERN_INFO
|
||||
"Choosing a random ethernet address for device %s\n", name);
|
||||
random_ether_addr(addr);
|
||||
eth_random_addr(addr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -309,6 +309,10 @@ void bpf_jit_compile(struct sk_filter *fp)
|
||||
else
|
||||
EMIT1_off32(0x0d, K); /* or imm32,%eax */
|
||||
break;
|
||||
case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
|
||||
seen |= SEEN_XREG;
|
||||
EMIT2(0x31, 0xd8); /* xor %ebx,%eax */
|
||||
break;
|
||||
case BPF_S_ALU_LSH_X: /* A <<= X; */
|
||||
seen |= SEEN_XREG;
|
||||
EMIT4(0x89, 0xd9, 0xd3, 0xe0); /* mov %ebx,%ecx; shl %cl,%eax */
|
||||
|
@ -496,9 +496,12 @@ static void crypto_netlink_rcv(struct sk_buff *skb)
|
||||
|
||||
static int __init crypto_user_init(void)
|
||||
{
|
||||
struct netlink_kernel_cfg cfg = {
|
||||
.input = crypto_netlink_rcv,
|
||||
};
|
||||
|
||||
crypto_nlsk = netlink_kernel_create(&init_net, NETLINK_CRYPTO,
|
||||
0, crypto_netlink_rcv,
|
||||
NULL, THIS_MODULE);
|
||||
THIS_MODULE, &cfg);
|
||||
if (!crypto_nlsk)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -46,6 +46,25 @@ config BCMA_DRIVER_MIPS
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config BCMA_SFLASH
|
||||
bool
|
||||
depends on BCMA_DRIVER_MIPS && BROKEN
|
||||
default y
|
||||
|
||||
config BCMA_NFLASH
|
||||
bool
|
||||
depends on BCMA_DRIVER_MIPS && BROKEN
|
||||
default y
|
||||
|
||||
config BCMA_DRIVER_GMAC_CMN
|
||||
bool "BCMA Broadcom GBIT MAC COMMON core driver"
|
||||
depends on BCMA
|
||||
help
|
||||
Driver for the Broadcom GBIT MAC COMMON core attached to Broadcom
|
||||
specific Advanced Microcontroller Bus.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config BCMA_DEBUG
|
||||
bool "BCMA debugging"
|
||||
depends on BCMA
|
||||
|
@ -1,8 +1,11 @@
|
||||
bcma-y += main.o scan.o core.o sprom.o
|
||||
bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
|
||||
bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o
|
||||
bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o
|
||||
bcma-y += driver_pci.o
|
||||
bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o
|
||||
bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
|
||||
bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o
|
||||
bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o
|
||||
bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o
|
||||
obj-$(CONFIG_BCMA) += bcma.o
|
||||
|
@ -10,6 +10,15 @@
|
||||
|
||||
#define BCMA_CORE_SIZE 0x1000
|
||||
|
||||
#define bcma_err(bus, fmt, ...) \
|
||||
pr_err("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
|
||||
#define bcma_warn(bus, fmt, ...) \
|
||||
pr_warn("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
|
||||
#define bcma_info(bus, fmt, ...) \
|
||||
pr_info("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
|
||||
#define bcma_debug(bus, fmt, ...) \
|
||||
pr_debug("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
|
||||
|
||||
struct bcma_bus;
|
||||
|
||||
/* main.c */
|
||||
@ -42,6 +51,28 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
|
||||
u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc);
|
||||
u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc);
|
||||
|
||||
#ifdef CONFIG_BCMA_SFLASH
|
||||
/* driver_chipcommon_sflash.c */
|
||||
int bcma_sflash_init(struct bcma_drv_cc *cc);
|
||||
#else
|
||||
static inline int bcma_sflash_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
bcma_err(cc->core->bus, "Serial flash not supported\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BCMA_SFLASH */
|
||||
|
||||
#ifdef CONFIG_BCMA_NFLASH
|
||||
/* driver_chipcommon_nflash.c */
|
||||
int bcma_nflash_init(struct bcma_drv_cc *cc);
|
||||
#else
|
||||
static inline int bcma_nflash_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
bcma_err(cc->core->bus, "NAND flash not supported\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BCMA_NFLASH */
|
||||
|
||||
#ifdef CONFIG_BCMA_HOST_PCI
|
||||
/* host_pci.c */
|
||||
extern int __init bcma_host_pci_init(void);
|
||||
|
@ -75,7 +75,7 @@ void bcma_core_set_clockmode(struct bcma_device *core,
|
||||
udelay(10);
|
||||
}
|
||||
if (i)
|
||||
pr_err("HT force timeout\n");
|
||||
bcma_err(core->bus, "HT force timeout\n");
|
||||
break;
|
||||
case BCMA_CLKMODE_DYNAMIC:
|
||||
bcma_set32(core, BCMA_CLKCTLST, ~BCMA_CLKCTLST_FORCEHT);
|
||||
@ -102,9 +102,9 @@ void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
|
||||
udelay(10);
|
||||
}
|
||||
if (i)
|
||||
pr_err("PLL enable timeout\n");
|
||||
bcma_err(core->bus, "PLL enable timeout\n");
|
||||
} else {
|
||||
pr_warn("Disabling PLL not supported yet!\n");
|
||||
bcma_warn(core->bus, "Disabling PLL not supported yet!\n");
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_core_pll_ctl);
|
||||
@ -120,8 +120,8 @@ u32 bcma_core_dma_translation(struct bcma_device *core)
|
||||
else
|
||||
return BCMA_DMA_TRANSLATION_DMA32_CMT;
|
||||
default:
|
||||
pr_err("DMA translation unknown for host %d\n",
|
||||
core->bus->hosttype);
|
||||
bcma_err(core->bus, "DMA translation unknown for host %d\n",
|
||||
core->bus->hosttype);
|
||||
}
|
||||
return BCMA_DMA_TRANSLATION_NONE;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
|
||||
if (cc->capabilities & BCMA_CC_CAP_PMU)
|
||||
bcma_pmu_init(cc);
|
||||
if (cc->capabilities & BCMA_CC_CAP_PCTL)
|
||||
pr_err("Power control not implemented!\n");
|
||||
bcma_err(cc->core->bus, "Power control not implemented!\n");
|
||||
|
||||
if (cc->core->id.rev >= 16) {
|
||||
if (cc->core->bus->sprom.leddc_on_time &&
|
||||
@ -137,8 +137,7 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
|
||||
| BCMA_CC_CORECTL_UARTCLKEN);
|
||||
}
|
||||
} else {
|
||||
pr_err("serial not supported on this device ccrev: 0x%x\n",
|
||||
ccrev);
|
||||
bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n", ccrev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
19
drivers/bcma/driver_chipcommon_nflash.c
Normal file
19
drivers/bcma/driver_chipcommon_nflash.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Broadcom specific AMBA
|
||||
* ChipCommon NAND flash interface
|
||||
*
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/bcma/bcma_driver_chipcommon.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "bcma_private.h"
|
||||
|
||||
/* Initialize NAND flash access */
|
||||
int bcma_nflash_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
bcma_err(cc->core->bus, "NAND flash support is broken\n");
|
||||
return 0;
|
||||
}
|
@ -3,7 +3,8 @@
|
||||
* ChipCommon Power Management Unit driver
|
||||
*
|
||||
* Copyright 2009, Michael Buesch <m@bues.ch>
|
||||
* Copyright 2007, Broadcom Corporation
|
||||
* Copyright 2007, 2011, Broadcom Corporation
|
||||
* Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
|
||||
*
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
@ -54,39 +55,19 @@ void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset);
|
||||
|
||||
static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4313:
|
||||
case 0x4331:
|
||||
case 43224:
|
||||
case 43225:
|
||||
break;
|
||||
default:
|
||||
pr_err("PLL init unknown for device 0x%04X\n",
|
||||
bus->chipinfo.id);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
u32 min_msk = 0, max_msk = 0;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4313:
|
||||
case BCMA_CHIP_ID_BCM4313:
|
||||
min_msk = 0x200D;
|
||||
max_msk = 0xFFFF;
|
||||
break;
|
||||
case 0x4331:
|
||||
case 43224:
|
||||
case 43225:
|
||||
break;
|
||||
default:
|
||||
pr_err("PMU resource config unknown for device 0x%04X\n",
|
||||
bus->chipinfo.id);
|
||||
bcma_debug(bus, "PMU resource config unknown or not needed for device 0x%04X\n",
|
||||
bus->chipinfo.id);
|
||||
}
|
||||
|
||||
/* Set the resource masks. */
|
||||
@ -94,22 +75,9 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
|
||||
bcma_cc_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk);
|
||||
if (max_msk)
|
||||
bcma_cc_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk);
|
||||
}
|
||||
|
||||
void bcma_pmu_swreg_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4313:
|
||||
case 0x4331:
|
||||
case 43224:
|
||||
case 43225:
|
||||
break;
|
||||
default:
|
||||
pr_err("PMU switch/regulators init unknown for device "
|
||||
"0x%04X\n", bus->chipinfo.id);
|
||||
}
|
||||
/* Add some delay; allow resources to come up and settle. */
|
||||
mdelay(2);
|
||||
}
|
||||
|
||||
/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */
|
||||
@ -123,8 +91,11 @@ void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable)
|
||||
val |= BCMA_CHIPCTL_4331_EXTPA_EN;
|
||||
if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11)
|
||||
val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
|
||||
else if (bus->chipinfo.rev > 0)
|
||||
val |= BCMA_CHIPCTL_4331_EXTPA_EN2;
|
||||
} else {
|
||||
val &= ~BCMA_CHIPCTL_4331_EXTPA_EN;
|
||||
val &= ~BCMA_CHIPCTL_4331_EXTPA_EN2;
|
||||
val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
|
||||
}
|
||||
bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val);
|
||||
@ -135,28 +106,38 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4313:
|
||||
bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
|
||||
case BCMA_CHIP_ID_BCM4313:
|
||||
/* enable 12 mA drive strenth for 4313 and set chipControl
|
||||
register bit 1 */
|
||||
bcma_chipco_chipctl_maskset(cc, 0,
|
||||
BCMA_CCTRL_4313_12MA_LED_DRIVE,
|
||||
BCMA_CCTRL_4313_12MA_LED_DRIVE);
|
||||
break;
|
||||
case 0x4331:
|
||||
case 43431:
|
||||
case BCMA_CHIP_ID_BCM4331:
|
||||
case BCMA_CHIP_ID_BCM43431:
|
||||
/* Ext PA lines must be enabled for tx on BCM4331 */
|
||||
bcma_chipco_bcm4331_ext_pa_lines_ctl(cc, true);
|
||||
break;
|
||||
case 43224:
|
||||
case BCMA_CHIP_ID_BCM43224:
|
||||
case BCMA_CHIP_ID_BCM43421:
|
||||
/* enable 12 mA drive strenth for 43224 and set chipControl
|
||||
register bit 15 */
|
||||
if (bus->chipinfo.rev == 0) {
|
||||
pr_err("Workarounds for 43224 rev 0 not fully "
|
||||
"implemented\n");
|
||||
bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x00F000F0);
|
||||
bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL,
|
||||
BCMA_CCTRL_43224_GPIO_TOGGLE,
|
||||
BCMA_CCTRL_43224_GPIO_TOGGLE);
|
||||
bcma_chipco_chipctl_maskset(cc, 0,
|
||||
BCMA_CCTRL_43224A0_12MA_LED_DRIVE,
|
||||
BCMA_CCTRL_43224A0_12MA_LED_DRIVE);
|
||||
} else {
|
||||
bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0);
|
||||
bcma_chipco_chipctl_maskset(cc, 0,
|
||||
BCMA_CCTRL_43224B0_12MA_LED_DRIVE,
|
||||
BCMA_CCTRL_43224B0_12MA_LED_DRIVE);
|
||||
}
|
||||
break;
|
||||
case 43225:
|
||||
break;
|
||||
default:
|
||||
pr_err("Workarounds unknown for device 0x%04X\n",
|
||||
bus->chipinfo.id);
|
||||
bcma_debug(bus, "Workarounds unknown or not needed for device 0x%04X\n",
|
||||
bus->chipinfo.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,8 +148,8 @@ void bcma_pmu_init(struct bcma_drv_cc *cc)
|
||||
pmucap = bcma_cc_read32(cc, BCMA_CC_PMU_CAP);
|
||||
cc->pmu.rev = (pmucap & BCMA_CC_PMU_CAP_REVISION);
|
||||
|
||||
pr_debug("Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev,
|
||||
pmucap);
|
||||
bcma_debug(cc->core->bus, "Found rev %u PMU (capabilities 0x%08X)\n",
|
||||
cc->pmu.rev, pmucap);
|
||||
|
||||
if (cc->pmu.rev == 1)
|
||||
bcma_cc_mask32(cc, BCMA_CC_PMU_CTL,
|
||||
@ -177,12 +158,7 @@ void bcma_pmu_init(struct bcma_drv_cc *cc)
|
||||
bcma_cc_set32(cc, BCMA_CC_PMU_CTL,
|
||||
BCMA_CC_PMU_CTL_NOILPONW);
|
||||
|
||||
if (cc->core->id.id == 0x4329 && cc->core->id.rev == 2)
|
||||
pr_err("Fix for 4329b0 bad LPOM state not implemented!\n");
|
||||
|
||||
bcma_pmu_pll_init(cc);
|
||||
bcma_pmu_resources_init(cc);
|
||||
bcma_pmu_swreg_init(cc);
|
||||
bcma_pmu_workarounds(cc);
|
||||
}
|
||||
|
||||
@ -191,23 +167,22 @@ u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc)
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4716:
|
||||
case 0x4748:
|
||||
case 47162:
|
||||
case 0x4313:
|
||||
case 0x5357:
|
||||
case 0x4749:
|
||||
case 53572:
|
||||
case BCMA_CHIP_ID_BCM4716:
|
||||
case BCMA_CHIP_ID_BCM4748:
|
||||
case BCMA_CHIP_ID_BCM47162:
|
||||
case BCMA_CHIP_ID_BCM4313:
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM4749:
|
||||
case BCMA_CHIP_ID_BCM53572:
|
||||
/* always 20Mhz */
|
||||
return 20000 * 1000;
|
||||
case 0x5356:
|
||||
case 0x5300:
|
||||
case BCMA_CHIP_ID_BCM5356:
|
||||
case BCMA_CHIP_ID_BCM4706:
|
||||
/* always 25Mhz */
|
||||
return 25000 * 1000;
|
||||
default:
|
||||
pr_warn("No ALP clock specified for %04X device, "
|
||||
"pmu rev. %d, using default %d Hz\n",
|
||||
bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
|
||||
bcma_warn(bus, "No ALP clock specified for %04X device, pmu rev. %d, using default %d Hz\n",
|
||||
bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
|
||||
}
|
||||
return BCMA_CC_PMU_ALP_CLOCK;
|
||||
}
|
||||
@ -224,7 +199,8 @@ static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m)
|
||||
|
||||
BUG_ON(!m || m > 4);
|
||||
|
||||
if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) {
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) {
|
||||
/* Detect failure in clock setting */
|
||||
tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
|
||||
if (tmp & 0x40000)
|
||||
@ -250,33 +226,62 @@ static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m)
|
||||
return (fc / div) * 1000000;
|
||||
}
|
||||
|
||||
static u32 bcma_pmu_clock_bcm4706(struct bcma_drv_cc *cc, u32 pll0, u32 m)
|
||||
{
|
||||
u32 tmp, ndiv, p1div, p2div;
|
||||
u32 clock;
|
||||
|
||||
BUG_ON(!m || m > 4);
|
||||
|
||||
/* Get N, P1 and P2 dividers to determine CPU clock */
|
||||
tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PMU6_4706_PROCPLL_OFF);
|
||||
ndiv = (tmp & BCMA_CC_PMU6_4706_PROC_NDIV_INT_MASK)
|
||||
>> BCMA_CC_PMU6_4706_PROC_NDIV_INT_SHIFT;
|
||||
p1div = (tmp & BCMA_CC_PMU6_4706_PROC_P1DIV_MASK)
|
||||
>> BCMA_CC_PMU6_4706_PROC_P1DIV_SHIFT;
|
||||
p2div = (tmp & BCMA_CC_PMU6_4706_PROC_P2DIV_MASK)
|
||||
>> BCMA_CC_PMU6_4706_PROC_P2DIV_SHIFT;
|
||||
|
||||
tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
|
||||
if (tmp & BCMA_CC_CHIPST_4706_PKG_OPTION)
|
||||
/* Low cost bonding: Fixed reference clock 25MHz and m = 4 */
|
||||
clock = (25000000 / 4) * ndiv * p2div / p1div;
|
||||
else
|
||||
/* Fixed reference clock 25MHz and m = 2 */
|
||||
clock = (25000000 / 2) * ndiv * p2div / p1div;
|
||||
|
||||
if (m == BCMA_CC_PMU5_MAINPLL_SSB)
|
||||
clock = clock / 4;
|
||||
|
||||
return clock;
|
||||
}
|
||||
|
||||
/* query bus clock frequency for PMU-enabled chipcommon */
|
||||
u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc)
|
||||
{
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4716:
|
||||
case 0x4748:
|
||||
case 47162:
|
||||
case BCMA_CHIP_ID_BCM4716:
|
||||
case BCMA_CHIP_ID_BCM4748:
|
||||
case BCMA_CHIP_ID_BCM47162:
|
||||
return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0,
|
||||
BCMA_CC_PMU5_MAINPLL_SSB);
|
||||
case 0x5356:
|
||||
case BCMA_CHIP_ID_BCM5356:
|
||||
return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0,
|
||||
BCMA_CC_PMU5_MAINPLL_SSB);
|
||||
case 0x5357:
|
||||
case 0x4749:
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM4749:
|
||||
return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0,
|
||||
BCMA_CC_PMU5_MAINPLL_SSB);
|
||||
case 0x5300:
|
||||
return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0,
|
||||
BCMA_CC_PMU5_MAINPLL_SSB);
|
||||
case 53572:
|
||||
case BCMA_CHIP_ID_BCM4706:
|
||||
return bcma_pmu_clock_bcm4706(cc, BCMA_CC_PMU4706_MAINPLL_PLL0,
|
||||
BCMA_CC_PMU5_MAINPLL_SSB);
|
||||
case BCMA_CHIP_ID_BCM53572:
|
||||
return 75000000;
|
||||
default:
|
||||
pr_warn("No backplane clock specified for %04X device, "
|
||||
"pmu rev. %d, using default %d Hz\n",
|
||||
bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK);
|
||||
bcma_warn(bus, "No backplane clock specified for %04X device, pmu rev. %d, using default %d Hz\n",
|
||||
bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK);
|
||||
}
|
||||
return BCMA_CC_PMU_HT_CLOCK;
|
||||
}
|
||||
@ -286,17 +291,21 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc)
|
||||
{
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
if (bus->chipinfo.id == 53572)
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM53572)
|
||||
return 300000000;
|
||||
|
||||
if (cc->pmu.rev >= 5) {
|
||||
u32 pll;
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x5356:
|
||||
case BCMA_CHIP_ID_BCM4706:
|
||||
return bcma_pmu_clock_bcm4706(cc,
|
||||
BCMA_CC_PMU4706_MAINPLL_PLL0,
|
||||
BCMA_CC_PMU5_MAINPLL_CPU);
|
||||
case BCMA_CHIP_ID_BCM5356:
|
||||
pll = BCMA_CC_PMU5356_MAINPLL_PLL0;
|
||||
break;
|
||||
case 0x5357:
|
||||
case 0x4749:
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM4749:
|
||||
pll = BCMA_CC_PMU5357_MAINPLL_PLL0;
|
||||
break;
|
||||
default:
|
||||
@ -304,10 +313,188 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc)
|
||||
break;
|
||||
}
|
||||
|
||||
/* TODO: if (bus->chipinfo.id == 0x5300)
|
||||
return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */
|
||||
return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU);
|
||||
}
|
||||
|
||||
return bcma_pmu_get_clockcontrol(cc);
|
||||
}
|
||||
|
||||
static void bcma_pmu_spuravoid_pll_write(struct bcma_drv_cc *cc, u32 offset,
|
||||
u32 value)
|
||||
{
|
||||
bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
|
||||
bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value);
|
||||
}
|
||||
|
||||
void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
||||
{
|
||||
u32 tmp = 0;
|
||||
u8 phypll_offset = 0;
|
||||
u8 bcm5357_bcm43236_p1div[] = {0x1, 0x5, 0x5};
|
||||
u8 bcm5357_bcm43236_ndiv[] = {0x30, 0xf6, 0xfc};
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM4749:
|
||||
case BCMA_CHIP_ID_BCM53572:
|
||||
/* 5357[ab]0, 43236[ab]0, and 6362b0 */
|
||||
|
||||
/* BCM5357 needs to touch PLL1_PLLCTL[02],
|
||||
so offset PLL0_PLLCTL[02] by 6 */
|
||||
phypll_offset = (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM4749 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) ? 6 : 0;
|
||||
|
||||
/* RMW only the P1 divider */
|
||||
bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR,
|
||||
BCMA_CC_PMU_PLL_CTL0 + phypll_offset);
|
||||
tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
|
||||
tmp &= (~(BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK));
|
||||
tmp |= (bcm5357_bcm43236_p1div[spuravoid] << BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT);
|
||||
bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp);
|
||||
|
||||
/* RMW only the int feedback divider */
|
||||
bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR,
|
||||
BCMA_CC_PMU_PLL_CTL2 + phypll_offset);
|
||||
tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
|
||||
tmp &= ~(BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK);
|
||||
tmp |= (bcm5357_bcm43236_ndiv[spuravoid]) << BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT;
|
||||
bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp);
|
||||
|
||||
tmp = 1 << 10;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM4331:
|
||||
case BCMA_CHIP_ID_BCM43431:
|
||||
if (spuravoid == 2) {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11500014);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x0FC00a08);
|
||||
} else if (spuravoid == 1) {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11500014);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x0F600a08);
|
||||
} else {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11100014);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x03000a08);
|
||||
}
|
||||
tmp = 1 << 10;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM43224:
|
||||
case BCMA_CHIP_ID_BCM43225:
|
||||
case BCMA_CHIP_ID_BCM43421:
|
||||
if (spuravoid == 1) {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11500010);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1,
|
||||
0x000C0C06);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x0F600a08);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3,
|
||||
0x00000000);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4,
|
||||
0x2001E920);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
} else {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11100010);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1,
|
||||
0x000c0c06);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x03000a08);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3,
|
||||
0x00000000);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4,
|
||||
0x200005c0);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
}
|
||||
tmp = 1 << 10;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM4716:
|
||||
case BCMA_CHIP_ID_BCM4748:
|
||||
case BCMA_CHIP_ID_BCM47162:
|
||||
if (spuravoid == 1) {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11500060);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1,
|
||||
0x080C0C06);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x0F600000);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3,
|
||||
0x00000000);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4,
|
||||
0x2001E924);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
} else {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11100060);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1,
|
||||
0x080c0c06);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x03000000);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3,
|
||||
0x00000000);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4,
|
||||
0x200005c0);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
}
|
||||
|
||||
tmp = 3 << 9;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM43227:
|
||||
case BCMA_CHIP_ID_BCM43228:
|
||||
case BCMA_CHIP_ID_BCM43428:
|
||||
/* LCNXN */
|
||||
/* PLL Settings for spur avoidance on/off mode,
|
||||
no on2 support for 43228A0 */
|
||||
if (spuravoid == 1) {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x01100014);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1,
|
||||
0x040C0C06);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x03140A08);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3,
|
||||
0x00333333);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4,
|
||||
0x202C2820);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
} else {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x11100014);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1,
|
||||
0x040c0c06);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x03000a08);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3,
|
||||
0x00000000);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4,
|
||||
0x200005c0);
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
}
|
||||
tmp = 1 << 10;
|
||||
break;
|
||||
default:
|
||||
bcma_err(bus, "Unknown spuravoidance settings for chip 0x%04X, not changing PLL\n",
|
||||
bus->chipinfo.id);
|
||||
break;
|
||||
}
|
||||
|
||||
tmp |= bcma_cc_read32(cc, BCMA_CC_PMU_CTL);
|
||||
bcma_cc_write32(cc, BCMA_CC_PMU_CTL, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_pmu_spuravoid_pllupdate);
|
||||
|
19
drivers/bcma/driver_chipcommon_sflash.c
Normal file
19
drivers/bcma/driver_chipcommon_sflash.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Broadcom specific AMBA
|
||||
* ChipCommon serial flash interface
|
||||
*
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/bcma/bcma_driver_chipcommon.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "bcma_private.h"
|
||||
|
||||
/* Initialize serial flash access */
|
||||
int bcma_sflash_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
bcma_err(cc->core->bus, "Serial flash support is broken\n");
|
||||
return 0;
|
||||
}
|
14
drivers/bcma/driver_gmac_cmn.c
Normal file
14
drivers/bcma/driver_gmac_cmn.c
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Broadcom specific AMBA
|
||||
* GBIT MAC COMMON Core
|
||||
*
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
|
||||
#include "bcma_private.h"
|
||||
#include <linux/bcma/bcma.h>
|
||||
|
||||
void __devinit bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc)
|
||||
{
|
||||
mutex_init(&gc->phy_mutex);
|
||||
}
|
@ -22,15 +22,15 @@
|
||||
/* The 47162a0 hangs when reading MIPS DMP registers registers */
|
||||
static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
|
||||
{
|
||||
return dev->bus->chipinfo.id == 47162 && dev->bus->chipinfo.rev == 0 &&
|
||||
dev->id.id == BCMA_CORE_MIPS_74K;
|
||||
return dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM47162 &&
|
||||
dev->bus->chipinfo.rev == 0 && dev->id.id == BCMA_CORE_MIPS_74K;
|
||||
}
|
||||
|
||||
/* The 5357b0 hangs when reading USB20H DMP registers */
|
||||
static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev)
|
||||
{
|
||||
return (dev->bus->chipinfo.id == 0x5357 ||
|
||||
dev->bus->chipinfo.id == 0x4749) &&
|
||||
return (dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 ||
|
||||
dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) &&
|
||||
dev->bus->chipinfo.pkg == 11 &&
|
||||
dev->id.id == BCMA_CORE_USB20_HOST;
|
||||
}
|
||||
@ -143,8 +143,8 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
|
||||
1 << irqflag);
|
||||
}
|
||||
|
||||
pr_info("set_irq: core 0x%04x, irq %d => %d\n",
|
||||
dev->id.id, oldirq + 2, irq + 2);
|
||||
bcma_info(bus, "set_irq: core 0x%04x, irq %d => %d\n",
|
||||
dev->id.id, oldirq + 2, irq + 2);
|
||||
}
|
||||
|
||||
static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq)
|
||||
@ -173,7 +173,7 @@ u32 bcma_cpu_clock(struct bcma_drv_mips *mcore)
|
||||
if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU)
|
||||
return bcma_pmu_get_clockcpu(&bus->drv_cc);
|
||||
|
||||
pr_err("No PMU available, need this to get the cpu clock\n");
|
||||
bcma_err(bus, "No PMU available, need this to get the cpu clock\n");
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bcma_cpu_clock);
|
||||
@ -185,10 +185,11 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
|
||||
switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) {
|
||||
case BCMA_CC_FLASHT_STSER:
|
||||
case BCMA_CC_FLASHT_ATSER:
|
||||
pr_err("Serial flash not supported.\n");
|
||||
bcma_debug(bus, "Found serial flash\n");
|
||||
bcma_sflash_init(&bus->drv_cc);
|
||||
break;
|
||||
case BCMA_CC_FLASHT_PARA:
|
||||
pr_info("found parallel flash.\n");
|
||||
bcma_debug(bus, "Found parallel flash\n");
|
||||
bus->drv_cc.pflash.window = 0x1c000000;
|
||||
bus->drv_cc.pflash.window_size = 0x02000000;
|
||||
|
||||
@ -199,7 +200,15 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
|
||||
bus->drv_cc.pflash.buswidth = 2;
|
||||
break;
|
||||
default:
|
||||
pr_err("flash not supported.\n");
|
||||
bcma_err(bus, "Flash type not supported\n");
|
||||
}
|
||||
|
||||
if (bus->drv_cc.core->id.rev == 38 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
|
||||
if (bus->drv_cc.capabilities & BCMA_CC_CAP_NFLASH) {
|
||||
bcma_debug(bus, "Found NAND flash\n");
|
||||
bcma_nflash_init(&bus->drv_cc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,7 +218,7 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore)
|
||||
struct bcma_device *core;
|
||||
bus = mcore->core->bus;
|
||||
|
||||
pr_info("Initializing MIPS core...\n");
|
||||
bcma_info(bus, "Initializing MIPS core...\n");
|
||||
|
||||
if (!mcore->setup_done)
|
||||
mcore->assigned_irqs = 1;
|
||||
@ -244,7 +253,7 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore)
|
||||
break;
|
||||
}
|
||||
}
|
||||
pr_info("IRQ reconfiguration done\n");
|
||||
bcma_info(bus, "IRQ reconfiguration done\n");
|
||||
bcma_core_mips_dump_irq(bus);
|
||||
|
||||
if (mcore->setup_done)
|
||||
|
@ -36,7 +36,7 @@ bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
|
||||
return false;
|
||||
|
||||
if (bus->sprom.boardflags_lo & BCMA_CORE_PCI_BFL_NOPCI) {
|
||||
pr_info("This PCI core is disabled and not working\n");
|
||||
bcma_info(bus, "This PCI core is disabled and not working\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -215,7 +215,8 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
|
||||
} else {
|
||||
writel(val, mmio);
|
||||
|
||||
if (chipid == 0x4716 || chipid == 0x4748)
|
||||
if (chipid == BCMA_CHIP_ID_BCM4716 ||
|
||||
chipid == BCMA_CHIP_ID_BCM4748)
|
||||
readl(mmio);
|
||||
}
|
||||
|
||||
@ -340,6 +341,7 @@ static u8 __devinit bcma_find_pci_capability(struct bcma_drv_pci *pc,
|
||||
*/
|
||||
static void __devinit bcma_core_pci_enable_crs(struct bcma_drv_pci *pc)
|
||||
{
|
||||
struct bcma_bus *bus = pc->core->bus;
|
||||
u8 cap_ptr, root_ctrl, root_cap, dev;
|
||||
u16 val16;
|
||||
int i;
|
||||
@ -378,7 +380,8 @@ static void __devinit bcma_core_pci_enable_crs(struct bcma_drv_pci *pc)
|
||||
udelay(10);
|
||||
}
|
||||
if (val16 == 0x1)
|
||||
pr_err("PCI: Broken device in slot %d\n", dev);
|
||||
bcma_err(bus, "PCI: Broken device in slot %d\n",
|
||||
dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -391,11 +394,11 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
|
||||
u32 pci_membase_1G;
|
||||
unsigned long io_map_base;
|
||||
|
||||
pr_info("PCIEcore in host mode found\n");
|
||||
bcma_info(bus, "PCIEcore in host mode found\n");
|
||||
|
||||
pc_host = kzalloc(sizeof(*pc_host), GFP_KERNEL);
|
||||
if (!pc_host) {
|
||||
pr_err("can not allocate memory");
|
||||
bcma_err(bus, "can not allocate memory");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -434,13 +437,14 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
|
||||
* as mips can't generate 64-bit address on the
|
||||
* backplane.
|
||||
*/
|
||||
if (bus->chipinfo.id == 0x4716 || bus->chipinfo.id == 0x4748) {
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4716 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM4748) {
|
||||
pc_host->mem_resource.start = BCMA_SOC_PCI_MEM;
|
||||
pc_host->mem_resource.end = BCMA_SOC_PCI_MEM +
|
||||
BCMA_SOC_PCI_MEM_SZ - 1;
|
||||
pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0,
|
||||
BCMA_CORE_PCI_SBTOPCI_MEM | BCMA_SOC_PCI_MEM);
|
||||
} else if (bus->chipinfo.id == 0x5300) {
|
||||
} else if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
|
||||
tmp = BCMA_CORE_PCI_SBTOPCI_MEM;
|
||||
tmp |= BCMA_CORE_PCI_SBTOPCI_PREF;
|
||||
tmp |= BCMA_CORE_PCI_SBTOPCI_BURST;
|
||||
|
@ -18,7 +18,7 @@ static void bcma_host_pci_switch_core(struct bcma_device *core)
|
||||
pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2,
|
||||
core->wrap);
|
||||
core->bus->mapped_core = core;
|
||||
pr_debug("Switched to core: 0x%X\n", core->id.id);
|
||||
bcma_debug(core->bus, "Switched to core: 0x%X\n", core->id.id);
|
||||
}
|
||||
|
||||
/* Provides access to the requested core. Returns base offset that has to be
|
||||
@ -188,7 +188,7 @@ static int __devinit bcma_host_pci_probe(struct pci_dev *dev,
|
||||
|
||||
/* SSB needed additional powering up, do we have any AMBA PCI cards? */
|
||||
if (!pci_is_pcie(dev))
|
||||
pr_err("PCI card detected, report problems.\n");
|
||||
bcma_err(bus, "PCI card detected, report problems.\n");
|
||||
|
||||
/* Map MMIO */
|
||||
err = -ENOMEM;
|
||||
@ -268,6 +268,7 @@ static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend,
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
|
||||
|
@ -61,6 +61,13 @@ static struct bus_type bcma_bus_type = {
|
||||
.dev_attrs = bcma_device_attrs,
|
||||
};
|
||||
|
||||
static u16 bcma_cc_core_id(struct bcma_bus *bus)
|
||||
{
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
|
||||
return BCMA_CORE_4706_CHIPCOMMON;
|
||||
return BCMA_CORE_CHIPCOMMON;
|
||||
}
|
||||
|
||||
struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid)
|
||||
{
|
||||
struct bcma_device *core;
|
||||
@ -91,10 +98,12 @@ static int bcma_register_cores(struct bcma_bus *bus)
|
||||
list_for_each_entry(core, &bus->cores, list) {
|
||||
/* We support that cores ourself */
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_4706_CHIPCOMMON:
|
||||
case BCMA_CORE_CHIPCOMMON:
|
||||
case BCMA_CORE_PCI:
|
||||
case BCMA_CORE_PCIE:
|
||||
case BCMA_CORE_MIPS_74K:
|
||||
case BCMA_CORE_4706_MAC_GBIT_COMMON:
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -118,8 +127,9 @@ static int bcma_register_cores(struct bcma_bus *bus)
|
||||
|
||||
err = device_register(&core->dev);
|
||||
if (err) {
|
||||
pr_err("Could not register dev for core 0x%03X\n",
|
||||
core->id.id);
|
||||
bcma_err(bus,
|
||||
"Could not register dev for core 0x%03X\n",
|
||||
core->id.id);
|
||||
continue;
|
||||
}
|
||||
core->dev_registered = true;
|
||||
@ -151,12 +161,12 @@ int __devinit bcma_bus_register(struct bcma_bus *bus)
|
||||
/* Scan for devices (cores) */
|
||||
err = bcma_bus_scan(bus);
|
||||
if (err) {
|
||||
pr_err("Failed to scan: %d\n", err);
|
||||
bcma_err(bus, "Failed to scan: %d\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init CC core */
|
||||
core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
|
||||
core = bcma_find_core(bus, bcma_cc_core_id(bus));
|
||||
if (core) {
|
||||
bus->drv_cc.core = core;
|
||||
bcma_core_chipcommon_init(&bus->drv_cc);
|
||||
@ -176,17 +186,24 @@ int __devinit bcma_bus_register(struct bcma_bus *bus)
|
||||
bcma_core_pci_init(&bus->drv_pci);
|
||||
}
|
||||
|
||||
/* Init GBIT MAC COMMON core */
|
||||
core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
|
||||
if (core) {
|
||||
bus->drv_gmac_cmn.core = core;
|
||||
bcma_core_gmac_cmn_init(&bus->drv_gmac_cmn);
|
||||
}
|
||||
|
||||
/* Try to get SPROM */
|
||||
err = bcma_sprom_get(bus);
|
||||
if (err == -ENOENT) {
|
||||
pr_err("No SPROM available\n");
|
||||
bcma_err(bus, "No SPROM available\n");
|
||||
} else if (err)
|
||||
pr_err("Failed to get SPROM: %d\n", err);
|
||||
bcma_err(bus, "Failed to get SPROM: %d\n", err);
|
||||
|
||||
/* Register found cores */
|
||||
bcma_register_cores(bus);
|
||||
|
||||
pr_info("Bus registered\n");
|
||||
bcma_info(bus, "Bus registered\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -207,14 +224,14 @@ int __init bcma_bus_early_register(struct bcma_bus *bus,
|
||||
bcma_init_bus(bus);
|
||||
|
||||
match.manuf = BCMA_MANUF_BCM;
|
||||
match.id = BCMA_CORE_CHIPCOMMON;
|
||||
match.id = bcma_cc_core_id(bus);
|
||||
match.class = BCMA_CL_SIM;
|
||||
match.rev = BCMA_ANY_REV;
|
||||
|
||||
/* Scan for chip common core */
|
||||
err = bcma_bus_scan_early(bus, &match, core_cc);
|
||||
if (err) {
|
||||
pr_err("Failed to scan for common core: %d\n", err);
|
||||
bcma_err(bus, "Failed to scan for common core: %d\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -226,12 +243,12 @@ int __init bcma_bus_early_register(struct bcma_bus *bus,
|
||||
/* Scan for mips core */
|
||||
err = bcma_bus_scan_early(bus, &match, core_mips);
|
||||
if (err) {
|
||||
pr_err("Failed to scan for mips core: %d\n", err);
|
||||
bcma_err(bus, "Failed to scan for mips core: %d\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init CC core */
|
||||
core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
|
||||
core = bcma_find_core(bus, bcma_cc_core_id(bus));
|
||||
if (core) {
|
||||
bus->drv_cc.core = core;
|
||||
bcma_core_chipcommon_init(&bus->drv_cc);
|
||||
@ -244,7 +261,7 @@ int __init bcma_bus_early_register(struct bcma_bus *bus,
|
||||
bcma_core_mips_init(&bus->drv_mips);
|
||||
}
|
||||
|
||||
pr_info("Early bus registered\n");
|
||||
bcma_info(bus, "Early bus registered\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -270,8 +287,7 @@ int bcma_bus_resume(struct bcma_bus *bus)
|
||||
struct bcma_device *core;
|
||||
|
||||
/* Init CC core */
|
||||
core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
|
||||
if (core) {
|
||||
if (bus->drv_cc.core) {
|
||||
bus->drv_cc.setup_done = false;
|
||||
bcma_core_chipcommon_init(&bus->drv_cc);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ struct bcma_device_id_name {
|
||||
};
|
||||
|
||||
static const struct bcma_device_id_name bcma_arm_device_names[] = {
|
||||
{ BCMA_CORE_4706_MAC_GBIT_COMMON, "BCM4706 GBit MAC Common" },
|
||||
{ BCMA_CORE_ARM_1176, "ARM 1176" },
|
||||
{ BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" },
|
||||
{ BCMA_CORE_ARM_CM3, "ARM CM3" },
|
||||
@ -28,6 +29,11 @@ static const struct bcma_device_id_name bcma_arm_device_names[] = {
|
||||
|
||||
static const struct bcma_device_id_name bcma_bcm_device_names[] = {
|
||||
{ BCMA_CORE_OOB_ROUTER, "OOB Router" },
|
||||
{ BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" },
|
||||
{ BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" },
|
||||
{ BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" },
|
||||
{ BCMA_CORE_AMEMC, "AMEMC (DDR)" },
|
||||
{ BCMA_CORE_ALTA, "ALTA (I2S)" },
|
||||
{ BCMA_CORE_INVALID, "Invalid" },
|
||||
{ BCMA_CORE_CHIPCOMMON, "ChipCommon" },
|
||||
{ BCMA_CORE_ILINE20, "ILine 20" },
|
||||
@ -289,11 +295,15 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
||||
|
||||
/* check if component is a core at all */
|
||||
if (wrappers[0] + wrappers[1] == 0) {
|
||||
/* we could save addrl of the router
|
||||
if (cid == BCMA_CORE_OOB_ROUTER)
|
||||
*/
|
||||
bcma_erom_skip_component(bus, eromptr);
|
||||
return -ENXIO;
|
||||
/* Some specific cores don't need wrappers */
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_4706_MAC_GBIT_COMMON:
|
||||
/* Not used yet: case BCMA_CORE_OOB_ROUTER: */
|
||||
break;
|
||||
default:
|
||||
bcma_erom_skip_component(bus, eromptr);
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (bcma_erom_is_bridge(bus, eromptr)) {
|
||||
@ -334,7 +344,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
||||
if (tmp <= 0) {
|
||||
return -EILSEQ;
|
||||
} else {
|
||||
pr_info("Bridge found\n");
|
||||
bcma_info(bus, "Bridge found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
@ -421,8 +431,8 @@ void bcma_init_bus(struct bcma_bus *bus)
|
||||
chipinfo->id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
|
||||
chipinfo->rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
|
||||
chipinfo->pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
|
||||
pr_info("Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n",
|
||||
chipinfo->id, chipinfo->rev, chipinfo->pkg);
|
||||
bcma_info(bus, "Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n",
|
||||
chipinfo->id, chipinfo->rev, chipinfo->pkg);
|
||||
|
||||
bus->init_done = true;
|
||||
}
|
||||
@ -476,13 +486,12 @@ int bcma_bus_scan(struct bcma_bus *bus)
|
||||
other_core = bcma_find_core_reverse(bus, core->id.id);
|
||||
core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1;
|
||||
|
||||
pr_info("Core %d found: %s "
|
||||
"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
|
||||
core->core_index, bcma_device_name(&core->id),
|
||||
core->id.manuf, core->id.id, core->id.rev,
|
||||
core->id.class);
|
||||
bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
|
||||
core->core_index, bcma_device_name(&core->id),
|
||||
core->id.manuf, core->id.id, core->id.rev,
|
||||
core->id.class);
|
||||
|
||||
list_add(&core->list, &bus->cores);
|
||||
list_add_tail(&core->list, &bus->cores);
|
||||
}
|
||||
|
||||
if (bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
@ -532,13 +541,12 @@ int __init bcma_bus_scan_early(struct bcma_bus *bus,
|
||||
|
||||
core->core_index = core_num++;
|
||||
bus->nr_cores++;
|
||||
pr_info("Core %d found: %s "
|
||||
"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
|
||||
core->core_index, bcma_device_name(&core->id),
|
||||
core->id.manuf, core->id.id, core->id.rev,
|
||||
core->id.class);
|
||||
bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
|
||||
core->core_index, bcma_device_name(&core->id),
|
||||
core->id.manuf, core->id.id, core->id.rev,
|
||||
core->id.class);
|
||||
|
||||
list_add(&core->list, &bus->cores);
|
||||
list_add_tail(&core->list, &bus->cores);
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
#define SCAN_CIB_NMW 0x0007C000
|
||||
#define SCAN_CIB_NMW_SHIFT 14
|
||||
#define SCAN_CIB_NSW 0x00F80000
|
||||
#define SCAN_CIB_NSW_SHIFT 17
|
||||
#define SCAN_CIB_NSW_SHIFT 19
|
||||
#define SCAN_CIB_REV 0xFF000000
|
||||
#define SCAN_CIB_REV_SHIFT 24
|
||||
|
||||
|
@ -60,11 +60,11 @@ static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
pr_debug("Using SPROM revision %d provided by"
|
||||
" platform.\n", bus->sprom.revision);
|
||||
bcma_debug(bus, "Using SPROM revision %d provided by platform.\n",
|
||||
bus->sprom.revision);
|
||||
return 0;
|
||||
fail:
|
||||
pr_warn("Using fallback SPROM failed (err %d)\n", err);
|
||||
bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -468,11 +468,11 @@ static bool bcma_sprom_ext_available(struct bcma_bus *bus)
|
||||
/* older chipcommon revisions use chip status register */
|
||||
chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4313:
|
||||
case BCMA_CHIP_ID_BCM4313:
|
||||
present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT;
|
||||
break;
|
||||
|
||||
case 0x4331:
|
||||
case BCMA_CHIP_ID_BCM4331:
|
||||
present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT;
|
||||
break;
|
||||
|
||||
@ -494,16 +494,16 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
|
||||
|
||||
chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
|
||||
switch (bus->chipinfo.id) {
|
||||
case 0x4313:
|
||||
case BCMA_CHIP_ID_BCM4313:
|
||||
present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT;
|
||||
break;
|
||||
|
||||
case 0x4331:
|
||||
case BCMA_CHIP_ID_BCM4331:
|
||||
present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
|
||||
break;
|
||||
|
||||
case 43224:
|
||||
case 43225:
|
||||
case BCMA_CHIP_ID_BCM43224:
|
||||
case BCMA_CHIP_ID_BCM43225:
|
||||
/* for these chips OTP is always available */
|
||||
present = true;
|
||||
break;
|
||||
@ -579,13 +579,15 @@ int bcma_sprom_get(struct bcma_bus *bus)
|
||||
if (!sprom)
|
||||
return -ENOMEM;
|
||||
|
||||
if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431)
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
|
||||
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
|
||||
|
||||
pr_debug("SPROM offset 0x%x\n", offset);
|
||||
bcma_debug(bus, "SPROM offset 0x%x\n", offset);
|
||||
bcma_sprom_read(bus, offset, sprom);
|
||||
|
||||
if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431)
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
|
||||
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
|
||||
|
||||
err = bcma_sprom_valid(sprom);
|
||||
|
@ -81,6 +81,18 @@ config BT_HCIUART_LL
|
||||
|
||||
Say Y here to compile support for HCILL protocol.
|
||||
|
||||
config BT_HCIUART_3WIRE
|
||||
bool "Three-wire UART (H5) protocol support"
|
||||
depends on BT_HCIUART
|
||||
help
|
||||
The HCI Three-wire UART Transport Layer makes it possible to
|
||||
user the Bluetooth HCI over a serial port interface. The HCI
|
||||
Three-wire UART Transport Layer assumes that the UART
|
||||
communication may have bit errors, overrun errors or burst
|
||||
errors and thereby making CTS/RTS lines unnecessary.
|
||||
|
||||
Say Y here to compile support for Three-wire UART protocol.
|
||||
|
||||
config BT_HCIBCM203X
|
||||
tristate "HCI BCM203x USB driver"
|
||||
depends on USB
|
||||
|
@ -28,4 +28,5 @@ hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o
|
||||
hci_uart-objs := $(hci_uart-y)
|
||||
|
@ -231,12 +231,12 @@ static void bluecard_write_wakeup(bluecard_info_t *info)
|
||||
}
|
||||
|
||||
do {
|
||||
register unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
register unsigned int offset;
|
||||
register unsigned char command;
|
||||
register unsigned long ready_bit;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
unsigned int offset;
|
||||
unsigned char command;
|
||||
unsigned long ready_bit;
|
||||
register struct sk_buff *skb;
|
||||
register int len;
|
||||
int len;
|
||||
|
||||
clear_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
|
||||
@ -621,7 +621,6 @@ static int bluecard_hci_flush(struct hci_dev *hdev)
|
||||
static int bluecard_hci_open(struct hci_dev *hdev)
|
||||
{
|
||||
bluecard_info_t *info = hci_get_drvdata(hdev);
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
|
||||
bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
|
||||
@ -630,6 +629,8 @@ static int bluecard_hci_open(struct hci_dev *hdev)
|
||||
return 0;
|
||||
|
||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
/* Enable LED */
|
||||
outb(0x08 | 0x20, iobase + 0x30);
|
||||
}
|
||||
@ -641,7 +642,6 @@ static int bluecard_hci_open(struct hci_dev *hdev)
|
||||
static int bluecard_hci_close(struct hci_dev *hdev)
|
||||
{
|
||||
bluecard_info_t *info = hci_get_drvdata(hdev);
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
|
||||
return 0;
|
||||
@ -649,6 +649,8 @@ static int bluecard_hci_close(struct hci_dev *hdev)
|
||||
bluecard_hci_flush(hdev);
|
||||
|
||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
/* Disable LED */
|
||||
outb(0x00, iobase + 0x30);
|
||||
}
|
||||
|
@ -470,7 +470,7 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *
|
||||
hdev->flush = bpa10x_flush;
|
||||
hdev->send = bpa10x_send_frame;
|
||||
|
||||
set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0) {
|
||||
|
@ -186,9 +186,9 @@ static void bt3c_write_wakeup(bt3c_info_t *info)
|
||||
return;
|
||||
|
||||
do {
|
||||
register unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
register struct sk_buff *skb;
|
||||
register int len;
|
||||
int len;
|
||||
|
||||
if (!pcmcia_dev_present(info->p_dev))
|
||||
break;
|
||||
@ -664,7 +664,7 @@ static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
int *try = priv_data;
|
||||
|
||||
if (try == 0)
|
||||
if (!try)
|
||||
p_dev->io_lines = 16;
|
||||
|
||||
if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
|
||||
|
@ -47,10 +47,11 @@ EXPORT_SYMBOL_GPL(btmrvl_interrupt);
|
||||
bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_event_hdr *hdr = (void *) skb->data;
|
||||
struct hci_ev_cmd_complete *ec;
|
||||
u16 opcode, ocf, ogf;
|
||||
|
||||
if (hdr->evt == HCI_EV_CMD_COMPLETE) {
|
||||
struct hci_ev_cmd_complete *ec;
|
||||
u16 opcode, ocf, ogf;
|
||||
|
||||
ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
|
||||
opcode = __le16_to_cpu(ec->opcode);
|
||||
ocf = hci_opcode_ocf(opcode);
|
||||
@ -64,7 +65,8 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
}
|
||||
|
||||
if (ogf == OGF) {
|
||||
BT_DBG("vendor event skipped: ogf 0x%4.4x", ogf);
|
||||
BT_DBG("vendor event skipped: ogf 0x%4.4x ocf 0x%4.4x",
|
||||
ogf, ocf);
|
||||
kfree_skb(skb);
|
||||
return false;
|
||||
}
|
||||
|
@ -110,6 +110,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = {
|
||||
/* Marvell SD8787 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
|
||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
|
||||
/* Marvell SD8787 Bluetooth AMP device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
|
||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
|
||||
/* Marvell SD8797 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
|
||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
|
||||
@ -565,8 +568,9 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
||||
if (type == HCI_EVENT_PKT) {
|
||||
if (btmrvl_check_evtpkt(priv, skb))
|
||||
hci_recv_frame(skb);
|
||||
} else
|
||||
} else {
|
||||
hci_recv_frame(skb);
|
||||
}
|
||||
|
||||
hdev->stat.byte_rx += buf_len;
|
||||
break;
|
||||
|
@ -140,9 +140,9 @@ static void btuart_write_wakeup(btuart_info_t *info)
|
||||
}
|
||||
|
||||
do {
|
||||
register unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
register struct sk_buff *skb;
|
||||
register int len;
|
||||
int len;
|
||||
|
||||
clear_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
|
||||
@ -593,7 +593,7 @@ static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
int *try = priv_data;
|
||||
|
||||
if (try == 0)
|
||||
if (!try)
|
||||
p_dev->io_lines = 16;
|
||||
|
||||
if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
|
||||
|
@ -21,15 +21,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
@ -1028,7 +1020,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
data->isoc = usb_ifnum_to_if(data->udev, 1);
|
||||
|
||||
if (!reset)
|
||||
set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
|
||||
if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
|
||||
if (!disable_scofix)
|
||||
@ -1040,7 +1032,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
if (id->driver_info & BTUSB_DIGIANSWER) {
|
||||
data->cmdreq_type = USB_TYPE_VENDOR;
|
||||
set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_CSR) {
|
||||
@ -1048,7 +1040,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
/* Old firmware would otherwise execute USB reset */
|
||||
if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117)
|
||||
set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_SNIFFER) {
|
||||
|
@ -144,9 +144,9 @@ static void dtl1_write_wakeup(dtl1_info_t *info)
|
||||
}
|
||||
|
||||
do {
|
||||
register unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
register struct sk_buff *skb;
|
||||
register int len;
|
||||
int len;
|
||||
|
||||
clear_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
|
||||
@ -586,29 +586,31 @@ static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data)
|
||||
static int dtl1_config(struct pcmcia_device *link)
|
||||
{
|
||||
dtl1_info_t *info = link->priv;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* Look for a generic full-sized window */
|
||||
link->resource[0]->end = 8;
|
||||
if (pcmcia_loop_config(link, dtl1_confcheck, NULL) < 0)
|
||||
ret = pcmcia_loop_config(link, dtl1_confcheck, NULL);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
i = pcmcia_request_irq(link, dtl1_interrupt);
|
||||
if (i != 0)
|
||||
ret = pcmcia_request_irq(link, dtl1_interrupt);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
i = pcmcia_enable_device(link);
|
||||
if (i != 0)
|
||||
ret = pcmcia_enable_device(link);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
if (dtl1_open(info) != 0)
|
||||
ret = dtl1_open(info);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
dtl1_detach(link);
|
||||
return -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pcmcia_device_id dtl1_ids[] = {
|
||||
|
@ -552,7 +552,7 @@ static u16 bscp_get_crc(struct bcsp_struct *bcsp)
|
||||
static int bcsp_recv(struct hci_uart *hu, void *data, int count)
|
||||
{
|
||||
struct bcsp_struct *bcsp = hu->priv;
|
||||
register unsigned char *ptr;
|
||||
unsigned char *ptr;
|
||||
|
||||
BT_DBG("hu %p count %d rx_state %d rx_count %ld",
|
||||
hu, count, bcsp->rx_state, bcsp->rx_count);
|
||||
|
@ -126,7 +126,7 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
|
||||
static inline int h4_check_data_len(struct h4_struct *h4, int len)
|
||||
{
|
||||
register int room = skb_tailroom(h4->rx_skb);
|
||||
int room = skb_tailroom(h4->rx_skb);
|
||||
|
||||
BT_DBG("len %d room %d", len, room);
|
||||
|
||||
|
747
drivers/bluetooth/hci_h5.c
Normal file
747
drivers/bluetooth/hci_h5.c
Normal file
@ -0,0 +1,747 @@
|
||||
/*
|
||||
*
|
||||
* Bluetooth HCI Three-wire UART driver
|
||||
*
|
||||
* Copyright (C) 2012 Intel 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 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 <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "hci_uart.h"
|
||||
|
||||
#define HCI_3WIRE_ACK_PKT 0
|
||||
#define HCI_3WIRE_LINK_PKT 15
|
||||
|
||||
/* Sliding window size */
|
||||
#define H5_TX_WIN_MAX 4
|
||||
|
||||
#define H5_ACK_TIMEOUT msecs_to_jiffies(250)
|
||||
#define H5_SYNC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
/*
|
||||
* Maximum Three-wire packet:
|
||||
* 4 byte header + max value for 12-bit length + 2 bytes for CRC
|
||||
*/
|
||||
#define H5_MAX_LEN (4 + 0xfff + 2)
|
||||
|
||||
/* Convenience macros for reading Three-wire header values */
|
||||
#define H5_HDR_SEQ(hdr) ((hdr)[0] & 0x07)
|
||||
#define H5_HDR_ACK(hdr) (((hdr)[0] >> 3) & 0x07)
|
||||
#define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01)
|
||||
#define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01)
|
||||
#define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f)
|
||||
#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0xff) + ((hdr)[2] << 4))
|
||||
|
||||
#define SLIP_DELIMITER 0xc0
|
||||
#define SLIP_ESC 0xdb
|
||||
#define SLIP_ESC_DELIM 0xdc
|
||||
#define SLIP_ESC_ESC 0xdd
|
||||
|
||||
/* H5 state flags */
|
||||
enum {
|
||||
H5_RX_ESC, /* SLIP escape mode */
|
||||
H5_TX_ACK_REQ, /* Pending ack to send */
|
||||
};
|
||||
|
||||
struct h5 {
|
||||
struct sk_buff_head unack; /* Unack'ed packets queue */
|
||||
struct sk_buff_head rel; /* Reliable packets queue */
|
||||
struct sk_buff_head unrel; /* Unreliable packets queue */
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
struct sk_buff *rx_skb; /* Receive buffer */
|
||||
size_t rx_pending; /* Expecting more bytes */
|
||||
u8 rx_ack; /* Last ack number received */
|
||||
|
||||
int (*rx_func) (struct hci_uart *hu, u8 c);
|
||||
|
||||
struct timer_list timer; /* Retransmission timer */
|
||||
|
||||
u8 tx_seq; /* Next seq number to send */
|
||||
u8 tx_ack; /* Next ack number to send */
|
||||
u8 tx_win; /* Sliding window size */
|
||||
|
||||
enum {
|
||||
H5_UNINITIALIZED,
|
||||
H5_INITIALIZED,
|
||||
H5_ACTIVE,
|
||||
} state;
|
||||
|
||||
enum {
|
||||
H5_AWAKE,
|
||||
H5_SLEEPING,
|
||||
H5_WAKING_UP,
|
||||
} sleep;
|
||||
};
|
||||
|
||||
static void h5_reset_rx(struct h5 *h5);
|
||||
|
||||
static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
struct sk_buff *nskb;
|
||||
|
||||
nskb = alloc_skb(3, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return;
|
||||
|
||||
bt_cb(nskb)->pkt_type = HCI_3WIRE_LINK_PKT;
|
||||
|
||||
memcpy(skb_put(nskb, len), data, len);
|
||||
|
||||
skb_queue_tail(&h5->unrel, nskb);
|
||||
}
|
||||
|
||||
static u8 h5_cfg_field(struct h5 *h5)
|
||||
{
|
||||
u8 field = 0;
|
||||
|
||||
/* Sliding window size (first 3 bits) */
|
||||
field |= (h5->tx_win & 7);
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
static void h5_timed_event(unsigned long arg)
|
||||
{
|
||||
const unsigned char sync_req[] = { 0x01, 0x7e };
|
||||
unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
|
||||
struct hci_uart *hu = (struct hci_uart *) arg;
|
||||
struct h5 *h5 = hu->priv;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
BT_DBG("%s", hu->hdev->name);
|
||||
|
||||
if (h5->state == H5_UNINITIALIZED)
|
||||
h5_link_control(hu, sync_req, sizeof(sync_req));
|
||||
|
||||
if (h5->state == H5_INITIALIZED) {
|
||||
conf_req[2] = h5_cfg_field(h5);
|
||||
h5_link_control(hu, conf_req, sizeof(conf_req));
|
||||
}
|
||||
|
||||
if (h5->state != H5_ACTIVE) {
|
||||
mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
|
||||
goto wakeup;
|
||||
}
|
||||
|
||||
if (h5->sleep != H5_AWAKE) {
|
||||
h5->sleep = H5_SLEEPING;
|
||||
goto wakeup;
|
||||
}
|
||||
|
||||
BT_DBG("hu %p retransmitting %u pkts", hu, h5->unack.qlen);
|
||||
|
||||
spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING);
|
||||
|
||||
while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) {
|
||||
h5->tx_seq = (h5->tx_seq - 1) & 0x07;
|
||||
skb_queue_head(&h5->rel, skb);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&h5->unack.lock, flags);
|
||||
|
||||
wakeup:
|
||||
hci_uart_tx_wakeup(hu);
|
||||
}
|
||||
|
||||
static int h5_open(struct hci_uart *hu)
|
||||
{
|
||||
struct h5 *h5;
|
||||
const unsigned char sync[] = { 0x01, 0x7e };
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
|
||||
if (!h5)
|
||||
return -ENOMEM;
|
||||
|
||||
hu->priv = h5;
|
||||
|
||||
skb_queue_head_init(&h5->unack);
|
||||
skb_queue_head_init(&h5->rel);
|
||||
skb_queue_head_init(&h5->unrel);
|
||||
|
||||
h5_reset_rx(h5);
|
||||
|
||||
init_timer(&h5->timer);
|
||||
h5->timer.function = h5_timed_event;
|
||||
h5->timer.data = (unsigned long) hu;
|
||||
|
||||
h5->tx_win = H5_TX_WIN_MAX;
|
||||
|
||||
set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
|
||||
|
||||
/* Send initial sync request */
|
||||
h5_link_control(hu, sync, sizeof(sync));
|
||||
mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_close(struct hci_uart *hu)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
|
||||
skb_queue_purge(&h5->unack);
|
||||
skb_queue_purge(&h5->rel);
|
||||
skb_queue_purge(&h5->unrel);
|
||||
|
||||
del_timer(&h5->timer);
|
||||
|
||||
kfree(h5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h5_pkt_cull(struct h5 *h5)
|
||||
{
|
||||
struct sk_buff *skb, *tmp;
|
||||
unsigned long flags;
|
||||
int i, to_remove;
|
||||
u8 seq;
|
||||
|
||||
spin_lock_irqsave(&h5->unack.lock, flags);
|
||||
|
||||
to_remove = skb_queue_len(&h5->unack);
|
||||
if (to_remove == 0)
|
||||
goto unlock;
|
||||
|
||||
seq = h5->tx_seq;
|
||||
|
||||
while (to_remove > 0) {
|
||||
if (h5->rx_ack == seq)
|
||||
break;
|
||||
|
||||
to_remove--;
|
||||
seq = (seq - 1) % 8;
|
||||
}
|
||||
|
||||
if (seq != h5->rx_ack)
|
||||
BT_ERR("Controller acked invalid packet");
|
||||
|
||||
i = 0;
|
||||
skb_queue_walk_safe(&h5->unack, skb, tmp) {
|
||||
if (i++ >= to_remove)
|
||||
break;
|
||||
|
||||
__skb_unlink(skb, &h5->unack);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
if (skb_queue_empty(&h5->unack))
|
||||
del_timer(&h5->timer);
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&h5->unack.lock, flags);
|
||||
}
|
||||
|
||||
static void h5_handle_internal_rx(struct hci_uart *hu)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
const unsigned char sync_req[] = { 0x01, 0x7e };
|
||||
const unsigned char sync_rsp[] = { 0x02, 0x7d };
|
||||
unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
|
||||
const unsigned char conf_rsp[] = { 0x04, 0x7b };
|
||||
const unsigned char wakeup_req[] = { 0x05, 0xfa };
|
||||
const unsigned char woken_req[] = { 0x06, 0xf9 };
|
||||
const unsigned char sleep_req[] = { 0x07, 0x78 };
|
||||
const unsigned char *hdr = h5->rx_skb->data;
|
||||
const unsigned char *data = &h5->rx_skb->data[4];
|
||||
|
||||
BT_DBG("%s", hu->hdev->name);
|
||||
|
||||
if (H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT)
|
||||
return;
|
||||
|
||||
if (H5_HDR_LEN(hdr) < 2)
|
||||
return;
|
||||
|
||||
conf_req[2] = h5_cfg_field(h5);
|
||||
|
||||
if (memcmp(data, sync_req, 2) == 0) {
|
||||
h5_link_control(hu, sync_rsp, 2);
|
||||
} else if (memcmp(data, sync_rsp, 2) == 0) {
|
||||
h5->state = H5_INITIALIZED;
|
||||
h5_link_control(hu, conf_req, 3);
|
||||
} else if (memcmp(data, conf_req, 2) == 0) {
|
||||
h5_link_control(hu, conf_rsp, 2);
|
||||
h5_link_control(hu, conf_req, 3);
|
||||
} else if (memcmp(data, conf_rsp, 2) == 0) {
|
||||
if (H5_HDR_LEN(hdr) > 2)
|
||||
h5->tx_win = (data[2] & 7);
|
||||
BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
|
||||
h5->state = H5_ACTIVE;
|
||||
hci_uart_init_ready(hu);
|
||||
return;
|
||||
} else if (memcmp(data, sleep_req, 2) == 0) {
|
||||
BT_DBG("Peer went to sleep");
|
||||
h5->sleep = H5_SLEEPING;
|
||||
return;
|
||||
} else if (memcmp(data, woken_req, 2) == 0) {
|
||||
BT_DBG("Peer woke up");
|
||||
h5->sleep = H5_AWAKE;
|
||||
} else if (memcmp(data, wakeup_req, 2) == 0) {
|
||||
BT_DBG("Peer requested wakeup");
|
||||
h5_link_control(hu, woken_req, 2);
|
||||
h5->sleep = H5_AWAKE;
|
||||
} else {
|
||||
BT_DBG("Link Control: 0x%02hhx 0x%02hhx", data[0], data[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
hci_uart_tx_wakeup(hu);
|
||||
}
|
||||
|
||||
static void h5_complete_rx_pkt(struct hci_uart *hu)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
const unsigned char *hdr = h5->rx_skb->data;
|
||||
|
||||
if (H5_HDR_RELIABLE(hdr)) {
|
||||
h5->tx_ack = (h5->tx_ack + 1) % 8;
|
||||
set_bit(H5_TX_ACK_REQ, &h5->flags);
|
||||
hci_uart_tx_wakeup(hu);
|
||||
}
|
||||
|
||||
h5->rx_ack = H5_HDR_ACK(hdr);
|
||||
|
||||
h5_pkt_cull(h5);
|
||||
|
||||
switch (H5_HDR_PKT_TYPE(hdr)) {
|
||||
case HCI_EVENT_PKT:
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
bt_cb(h5->rx_skb)->pkt_type = H5_HDR_PKT_TYPE(hdr);
|
||||
|
||||
/* Remove Three-wire header */
|
||||
skb_pull(h5->rx_skb, 4);
|
||||
|
||||
hci_recv_frame(h5->rx_skb);
|
||||
h5->rx_skb = NULL;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
h5_handle_internal_rx(hu);
|
||||
break;
|
||||
}
|
||||
|
||||
h5_reset_rx(h5);
|
||||
}
|
||||
|
||||
static int h5_rx_crc(struct hci_uart *hu, unsigned char c)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
|
||||
h5_complete_rx_pkt(hu);
|
||||
h5_reset_rx(h5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_rx_payload(struct hci_uart *hu, unsigned char c)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
const unsigned char *hdr = h5->rx_skb->data;
|
||||
|
||||
if (H5_HDR_CRC(hdr)) {
|
||||
h5->rx_func = h5_rx_crc;
|
||||
h5->rx_pending = 2;
|
||||
} else {
|
||||
h5_complete_rx_pkt(hu);
|
||||
h5_reset_rx(h5);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
const unsigned char *hdr = h5->rx_skb->data;
|
||||
|
||||
BT_DBG("%s rx: seq %u ack %u crc %u rel %u type %u len %u",
|
||||
hu->hdev->name, H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr),
|
||||
H5_HDR_CRC(hdr), H5_HDR_RELIABLE(hdr), H5_HDR_PKT_TYPE(hdr),
|
||||
H5_HDR_LEN(hdr));
|
||||
|
||||
if (((hdr[0] + hdr[1] + hdr[2] + hdr[3]) & 0xff) != 0xff) {
|
||||
BT_ERR("Invalid header checksum");
|
||||
h5_reset_rx(h5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != h5->tx_ack) {
|
||||
BT_ERR("Out-of-order packet arrived (%u != %u)",
|
||||
H5_HDR_SEQ(hdr), h5->tx_ack);
|
||||
h5_reset_rx(h5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (h5->state != H5_ACTIVE &&
|
||||
H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) {
|
||||
BT_ERR("Non-link packet received in non-active state");
|
||||
h5_reset_rx(h5);
|
||||
}
|
||||
|
||||
h5->rx_func = h5_rx_payload;
|
||||
h5->rx_pending = H5_HDR_LEN(hdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
|
||||
if (c == SLIP_DELIMITER)
|
||||
return 1;
|
||||
|
||||
h5->rx_func = h5_rx_3wire_hdr;
|
||||
h5->rx_pending = 4;
|
||||
|
||||
h5->rx_skb = bt_skb_alloc(H5_MAX_LEN, GFP_ATOMIC);
|
||||
if (!h5->rx_skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
h5_reset_rx(h5);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
h5->rx_skb->dev = (void *) hu->hdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_rx_delimiter(struct hci_uart *hu, unsigned char c)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
|
||||
if (c == SLIP_DELIMITER)
|
||||
h5->rx_func = h5_rx_pkt_start;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void h5_unslip_one_byte(struct h5 *h5, unsigned char c)
|
||||
{
|
||||
const u8 delim = SLIP_DELIMITER, esc = SLIP_ESC;
|
||||
const u8 *byte = &c;
|
||||
|
||||
if (!test_bit(H5_RX_ESC, &h5->flags) && c == SLIP_ESC) {
|
||||
set_bit(H5_RX_ESC, &h5->flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(H5_RX_ESC, &h5->flags)) {
|
||||
switch (c) {
|
||||
case SLIP_ESC_DELIM:
|
||||
byte = &delim;
|
||||
break;
|
||||
case SLIP_ESC_ESC:
|
||||
byte = &esc;
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Invalid esc byte 0x%02hhx", c);
|
||||
h5_reset_rx(h5);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(skb_put(h5->rx_skb, 1), byte, 1);
|
||||
h5->rx_pending--;
|
||||
|
||||
BT_DBG("unsliped 0x%02hhx, rx_pending %zu", *byte, h5->rx_pending);
|
||||
}
|
||||
|
||||
static void h5_reset_rx(struct h5 *h5)
|
||||
{
|
||||
if (h5->rx_skb) {
|
||||
kfree_skb(h5->rx_skb);
|
||||
h5->rx_skb = NULL;
|
||||
}
|
||||
|
||||
h5->rx_func = h5_rx_delimiter;
|
||||
h5->rx_pending = 0;
|
||||
clear_bit(H5_RX_ESC, &h5->flags);
|
||||
}
|
||||
|
||||
static int h5_recv(struct hci_uart *hu, void *data, int count)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
unsigned char *ptr = data;
|
||||
|
||||
BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending,
|
||||
count);
|
||||
|
||||
while (count > 0) {
|
||||
int processed;
|
||||
|
||||
if (h5->rx_pending > 0) {
|
||||
if (*ptr == SLIP_DELIMITER) {
|
||||
BT_ERR("Too short H5 packet");
|
||||
h5_reset_rx(h5);
|
||||
continue;
|
||||
}
|
||||
|
||||
h5_unslip_one_byte(h5, *ptr);
|
||||
|
||||
ptr++; count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
processed = h5->rx_func(hu, *ptr);
|
||||
if (processed < 0)
|
||||
return processed;
|
||||
|
||||
ptr += processed;
|
||||
count -= processed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
|
||||
if (skb->len > 0xfff) {
|
||||
BT_ERR("Packet too long (%u bytes)", skb->len);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (h5->state != H5_ACTIVE) {
|
||||
BT_ERR("Ignoring HCI data in non-active state");
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (bt_cb(skb)->pkt_type) {
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_COMMAND_PKT:
|
||||
skb_queue_tail(&h5->rel, skb);
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
skb_queue_tail(&h5->unrel, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown packet type %u", bt_cb(skb)->pkt_type);
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h5_slip_delim(struct sk_buff *skb)
|
||||
{
|
||||
const char delim = SLIP_DELIMITER;
|
||||
|
||||
memcpy(skb_put(skb, 1), &delim, 1);
|
||||
}
|
||||
|
||||
static void h5_slip_one_byte(struct sk_buff *skb, u8 c)
|
||||
{
|
||||
const char esc_delim[2] = { SLIP_ESC, SLIP_ESC_DELIM };
|
||||
const char esc_esc[2] = { SLIP_ESC, SLIP_ESC_ESC };
|
||||
|
||||
switch (c) {
|
||||
case SLIP_DELIMITER:
|
||||
memcpy(skb_put(skb, 2), &esc_delim, 2);
|
||||
break;
|
||||
case SLIP_ESC:
|
||||
memcpy(skb_put(skb, 2), &esc_esc, 2);
|
||||
break;
|
||||
default:
|
||||
memcpy(skb_put(skb, 1), &c, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static bool valid_packet_type(u8 type)
|
||||
{
|
||||
switch (type) {
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_COMMAND_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
case HCI_3WIRE_LINK_PKT:
|
||||
case HCI_3WIRE_ACK_PKT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
|
||||
const u8 *data, size_t len)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
struct sk_buff *nskb;
|
||||
u8 hdr[4];
|
||||
int i;
|
||||
|
||||
if (!valid_packet_type(pkt_type)) {
|
||||
BT_ERR("Unknown packet type %u", pkt_type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Max len of packet: (original len + 4 (H5 hdr) + 2 (crc)) * 2
|
||||
* (because bytes 0xc0 and 0xdb are escaped, worst case is when
|
||||
* the packet is all made of 0xc0 and 0xdb) + 2 (0xc0
|
||||
* delimiters at start and end).
|
||||
*/
|
||||
nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return NULL;
|
||||
|
||||
bt_cb(nskb)->pkt_type = pkt_type;
|
||||
|
||||
h5_slip_delim(nskb);
|
||||
|
||||
hdr[0] = h5->tx_ack << 3;
|
||||
clear_bit(H5_TX_ACK_REQ, &h5->flags);
|
||||
|
||||
/* Reliable packet? */
|
||||
if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) {
|
||||
hdr[0] |= 1 << 7;
|
||||
hdr[0] |= h5->tx_seq;
|
||||
h5->tx_seq = (h5->tx_seq + 1) % 8;
|
||||
}
|
||||
|
||||
hdr[1] = pkt_type | ((len & 0x0f) << 4);
|
||||
hdr[2] = len >> 4;
|
||||
hdr[3] = ~((hdr[0] + hdr[1] + hdr[2]) & 0xff);
|
||||
|
||||
BT_DBG("%s tx: seq %u ack %u crc %u rel %u type %u len %u",
|
||||
hu->hdev->name, H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr),
|
||||
H5_HDR_CRC(hdr), H5_HDR_RELIABLE(hdr), H5_HDR_PKT_TYPE(hdr),
|
||||
H5_HDR_LEN(hdr));
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
h5_slip_one_byte(nskb, hdr[i]);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
h5_slip_one_byte(nskb, data[i]);
|
||||
|
||||
h5_slip_delim(nskb);
|
||||
|
||||
return nskb;
|
||||
}
|
||||
|
||||
static struct sk_buff *h5_dequeue(struct hci_uart *hu)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb, *nskb;
|
||||
|
||||
if (h5->sleep != H5_AWAKE) {
|
||||
const unsigned char wakeup_req[] = { 0x05, 0xfa };
|
||||
|
||||
if (h5->sleep == H5_WAKING_UP)
|
||||
return NULL;
|
||||
|
||||
h5->sleep = H5_WAKING_UP;
|
||||
BT_DBG("Sending wakeup request");
|
||||
|
||||
mod_timer(&h5->timer, jiffies + HZ / 100);
|
||||
return h5_prepare_pkt(hu, HCI_3WIRE_LINK_PKT, wakeup_req, 2);
|
||||
}
|
||||
|
||||
if ((skb = skb_dequeue(&h5->unrel)) != NULL) {
|
||||
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
|
||||
skb->data, skb->len);
|
||||
if (nskb) {
|
||||
kfree_skb(skb);
|
||||
return nskb;
|
||||
}
|
||||
|
||||
skb_queue_head(&h5->unrel, skb);
|
||||
BT_ERR("Could not dequeue pkt because alloc_skb failed");
|
||||
}
|
||||
|
||||
spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING);
|
||||
|
||||
if (h5->unack.qlen >= h5->tx_win)
|
||||
goto unlock;
|
||||
|
||||
if ((skb = skb_dequeue(&h5->rel)) != NULL) {
|
||||
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
|
||||
skb->data, skb->len);
|
||||
if (nskb) {
|
||||
__skb_queue_tail(&h5->unack, skb);
|
||||
mod_timer(&h5->timer, jiffies + H5_ACK_TIMEOUT);
|
||||
spin_unlock_irqrestore(&h5->unack.lock, flags);
|
||||
return nskb;
|
||||
}
|
||||
|
||||
skb_queue_head(&h5->rel, skb);
|
||||
BT_ERR("Could not dequeue pkt because alloc_skb failed");
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&h5->unack.lock, flags);
|
||||
|
||||
if (test_bit(H5_TX_ACK_REQ, &h5->flags))
|
||||
return h5_prepare_pkt(hu, HCI_3WIRE_ACK_PKT, NULL, 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int h5_flush(struct hci_uart *hu)
|
||||
{
|
||||
BT_DBG("hu %p", hu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hci_uart_proto h5p = {
|
||||
.id = HCI_UART_3WIRE,
|
||||
.open = h5_open,
|
||||
.close = h5_close,
|
||||
.recv = h5_recv,
|
||||
.enqueue = h5_enqueue,
|
||||
.dequeue = h5_dequeue,
|
||||
.flush = h5_flush,
|
||||
};
|
||||
|
||||
int __init h5_init(void)
|
||||
{
|
||||
int err = hci_uart_register_proto(&h5p);
|
||||
|
||||
if (!err)
|
||||
BT_INFO("HCI Three-wire UART (H5) protocol initialized");
|
||||
else
|
||||
BT_ERR("HCI Three-wire UART (H5) protocol init failed");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int __exit h5_deinit(void)
|
||||
{
|
||||
return hci_uart_unregister_proto(&h5p);
|
||||
}
|
@ -156,6 +156,35 @@ restart:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hci_uart_init_work(struct work_struct *work)
|
||||
{
|
||||
struct hci_uart *hu = container_of(work, struct hci_uart, init_ready);
|
||||
int err;
|
||||
|
||||
if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
|
||||
return;
|
||||
|
||||
err = hci_register_dev(hu->hdev);
|
||||
if (err < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
hci_free_dev(hu->hdev);
|
||||
hu->hdev = NULL;
|
||||
hu->proto->close(hu);
|
||||
}
|
||||
|
||||
set_bit(HCI_UART_REGISTERED, &hu->flags);
|
||||
}
|
||||
|
||||
int hci_uart_init_ready(struct hci_uart *hu)
|
||||
{
|
||||
if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
|
||||
return -EALREADY;
|
||||
|
||||
schedule_work(&hu->init_ready);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------- Interface to HCI layer ------ */
|
||||
/* Initialize device */
|
||||
static int hci_uart_open(struct hci_dev *hdev)
|
||||
@ -264,6 +293,8 @@ static int hci_uart_tty_open(struct tty_struct *tty)
|
||||
hu->tty = tty;
|
||||
tty->receive_room = 65536;
|
||||
|
||||
INIT_WORK(&hu->init_ready, hci_uart_init_work);
|
||||
|
||||
spin_lock_init(&hu->rx_lock);
|
||||
|
||||
/* Flush any pending characters in the driver and line discipline. */
|
||||
@ -286,28 +317,30 @@ static int hci_uart_tty_open(struct tty_struct *tty)
|
||||
static void hci_uart_tty_close(struct tty_struct *tty)
|
||||
{
|
||||
struct hci_uart *hu = (void *)tty->disc_data;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
BT_DBG("tty %p", tty);
|
||||
|
||||
/* Detach from the tty */
|
||||
tty->disc_data = NULL;
|
||||
|
||||
if (hu) {
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
if (!hu)
|
||||
return;
|
||||
|
||||
if (hdev)
|
||||
hci_uart_close(hdev);
|
||||
hdev = hu->hdev;
|
||||
if (hdev)
|
||||
hci_uart_close(hdev);
|
||||
|
||||
if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
|
||||
if (hdev) {
|
||||
if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
|
||||
if (hdev) {
|
||||
if (test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
}
|
||||
hu->proto->close(hu);
|
||||
hci_free_dev(hdev);
|
||||
}
|
||||
|
||||
kfree(hu);
|
||||
hu->proto->close(hu);
|
||||
}
|
||||
|
||||
kfree(hu);
|
||||
}
|
||||
|
||||
/* hci_uart_tty_wakeup()
|
||||
@ -394,19 +427,24 @@ static int hci_uart_register_dev(struct hci_uart *hu)
|
||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||
|
||||
if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
|
||||
set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
|
||||
if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
|
||||
hdev->dev_type = HCI_AMP;
|
||||
else
|
||||
hdev->dev_type = HCI_BREDR;
|
||||
|
||||
if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
|
||||
return 0;
|
||||
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
set_bit(HCI_UART_REGISTERED, &hu->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -558,6 +596,9 @@ static int __init hci_uart_init(void)
|
||||
#ifdef CONFIG_BT_HCIUART_ATH3K
|
||||
ath_init();
|
||||
#endif
|
||||
#ifdef CONFIG_BT_HCIUART_3WIRE
|
||||
h5_init();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -578,6 +619,9 @@ static void __exit hci_uart_exit(void)
|
||||
#ifdef CONFIG_BT_HCIUART_ATH3K
|
||||
ath_deinit();
|
||||
#endif
|
||||
#ifdef CONFIG_BT_HCIUART_3WIRE
|
||||
h5_deinit();
|
||||
#endif
|
||||
|
||||
/* Release tty registration of line discipline */
|
||||
if ((err = tty_unregister_ldisc(N_HCI)))
|
||||
|
@ -348,7 +348,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
|
||||
static inline int ll_check_data_len(struct ll_struct *ll, int len)
|
||||
{
|
||||
register int room = skb_tailroom(ll->rx_skb);
|
||||
int room = skb_tailroom(ll->rx_skb);
|
||||
|
||||
BT_DBG("len %d room %d", len, room);
|
||||
|
||||
@ -374,11 +374,11 @@ static inline int ll_check_data_len(struct ll_struct *ll, int len)
|
||||
static int ll_recv(struct hci_uart *hu, void *data, int count)
|
||||
{
|
||||
struct ll_struct *ll = hu->priv;
|
||||
register char *ptr;
|
||||
char *ptr;
|
||||
struct hci_event_hdr *eh;
|
||||
struct hci_acl_hdr *ah;
|
||||
struct hci_sco_hdr *sh;
|
||||
register int len, type, dlen;
|
||||
int len, type, dlen;
|
||||
|
||||
BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count);
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
#define HCI_UART_RAW_DEVICE 0
|
||||
#define HCI_UART_RESET_ON_INIT 1
|
||||
#define HCI_UART_CREATE_AMP 2
|
||||
#define HCI_UART_INIT_PENDING 3
|
||||
|
||||
struct hci_uart;
|
||||
|
||||
@ -66,6 +67,8 @@ struct hci_uart {
|
||||
unsigned long flags;
|
||||
unsigned long hdev_flags;
|
||||
|
||||
struct work_struct init_ready;
|
||||
|
||||
struct hci_uart_proto *proto;
|
||||
void *priv;
|
||||
|
||||
@ -76,6 +79,7 @@ struct hci_uart {
|
||||
|
||||
/* HCI_UART proto flag bits */
|
||||
#define HCI_UART_PROTO_SET 0
|
||||
#define HCI_UART_REGISTERED 1
|
||||
|
||||
/* TX states */
|
||||
#define HCI_UART_SENDING 1
|
||||
@ -84,6 +88,7 @@ struct hci_uart {
|
||||
int hci_uart_register_proto(struct hci_uart_proto *p);
|
||||
int hci_uart_unregister_proto(struct hci_uart_proto *p);
|
||||
int hci_uart_tx_wakeup(struct hci_uart *hu);
|
||||
int hci_uart_init_ready(struct hci_uart *hu);
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_H4
|
||||
int h4_init(void);
|
||||
@ -104,3 +109,8 @@ int ll_deinit(void);
|
||||
int ath_init(void);
|
||||
int ath_deinit(void);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_3WIRE
|
||||
int h5_init(void);
|
||||
int h5_deinit(void);
|
||||
#endif
|
||||
|
@ -46,7 +46,7 @@ static DEFINE_PER_CPU(__u32, proc_event_counts) = { 0 };
|
||||
static inline void get_seq(__u32 *ts, int *cpu)
|
||||
{
|
||||
preempt_disable();
|
||||
*ts = __this_cpu_inc_return(proc_event_counts) -1;
|
||||
*ts = __this_cpu_inc_return(proc_event_counts) - 1;
|
||||
*cpu = smp_processor_id();
|
||||
preempt_enable();
|
||||
}
|
||||
@ -62,8 +62,8 @@ void proc_fork_connector(struct task_struct *task)
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
msg = (struct cn_msg *)buffer;
|
||||
ev = (struct proc_event *)msg->data;
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
ktime_get_ts(&ts); /* get high res monotonic timestamp */
|
||||
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
|
||||
@ -93,8 +93,8 @@ void proc_exec_connector(struct task_struct *task)
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
msg = (struct cn_msg *)buffer;
|
||||
ev = (struct proc_event *)msg->data;
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
ktime_get_ts(&ts); /* get high res monotonic timestamp */
|
||||
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
|
||||
@ -119,8 +119,8 @@ void proc_id_connector(struct task_struct *task, int which_id)
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
msg = (struct cn_msg *)buffer;
|
||||
ev = (struct proc_event *)msg->data;
|
||||
ev->what = which_id;
|
||||
ev->event_data.id.process_pid = task->pid;
|
||||
ev->event_data.id.process_tgid = task->tgid;
|
||||
@ -134,7 +134,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
|
||||
ev->event_data.id.e.egid = cred->egid;
|
||||
} else {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
return;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
@ -241,8 +241,8 @@ void proc_exit_connector(struct task_struct *task)
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
msg = (struct cn_msg *)buffer;
|
||||
ev = (struct proc_event *)msg->data;
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
ktime_get_ts(&ts); /* get high res monotonic timestamp */
|
||||
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
|
||||
@ -276,8 +276,8 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
msg = (struct cn_msg *)buffer;
|
||||
ev = (struct proc_event *)msg->data;
|
||||
msg->seq = rcvd_seq;
|
||||
ktime_get_ts(&ts); /* get high res monotonic timestamp */
|
||||
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
|
||||
@ -303,7 +303,7 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
|
||||
if (msg->len != sizeof(*mc_op))
|
||||
return;
|
||||
|
||||
mc_op = (enum proc_cn_mcast_op*)msg->data;
|
||||
mc_op = (enum proc_cn_mcast_op *)msg->data;
|
||||
switch (*mc_op) {
|
||||
case PROC_CN_MCAST_LISTEN:
|
||||
atomic_inc(&proc_event_num_listeners);
|
||||
@ -325,11 +325,11 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
|
||||
*/
|
||||
static int __init cn_proc_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = cn_add_callback(&cn_proc_event_id, "cn_proc",
|
||||
&cn_proc_mcast_ctl))) {
|
||||
printk(KERN_WARNING "cn_proc failed to register\n");
|
||||
int err = cn_add_callback(&cn_proc_event_id,
|
||||
"cn_proc",
|
||||
&cn_proc_mcast_ctl);
|
||||
if (err) {
|
||||
pr_warn("cn_proc failed to register\n");
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* cn_queue.c
|
||||
* cn_queue.c
|
||||
*
|
||||
* 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
|
||||
* All rights reserved.
|
||||
@ -34,13 +34,14 @@
|
||||
static struct cn_callback_entry *
|
||||
cn_queue_alloc_callback_entry(struct cn_queue_dev *dev, const char *name,
|
||||
struct cb_id *id,
|
||||
void (*callback)(struct cn_msg *, struct netlink_skb_parms *))
|
||||
void (*callback)(struct cn_msg *,
|
||||
struct netlink_skb_parms *))
|
||||
{
|
||||
struct cn_callback_entry *cbq;
|
||||
|
||||
cbq = kzalloc(sizeof(*cbq), GFP_KERNEL);
|
||||
if (!cbq) {
|
||||
printk(KERN_ERR "Failed to create new callback queue.\n");
|
||||
pr_err("Failed to create new callback queue.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -71,7 +72,8 @@ int cn_cb_equal(struct cb_id *i1, struct cb_id *i2)
|
||||
|
||||
int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
|
||||
struct cb_id *id,
|
||||
void (*callback)(struct cn_msg *, struct netlink_skb_parms *))
|
||||
void (*callback)(struct cn_msg *,
|
||||
struct netlink_skb_parms *))
|
||||
{
|
||||
struct cn_callback_entry *cbq, *__cbq;
|
||||
int found = 0;
|
||||
@ -149,7 +151,7 @@ void cn_queue_free_dev(struct cn_queue_dev *dev)
|
||||
spin_unlock_bh(&dev->queue_lock);
|
||||
|
||||
while (atomic_read(&dev->refcnt)) {
|
||||
printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
|
||||
pr_info("Waiting for %s to become free: refcnt=%d.\n",
|
||||
dev->name, atomic_read(&dev->refcnt));
|
||||
msleep(1000);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* connector.c
|
||||
* connector.c
|
||||
*
|
||||
* 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
|
||||
* All rights reserved.
|
||||
@ -101,19 +101,19 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
nlh = NLMSG_PUT(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh));
|
||||
nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh), 0);
|
||||
if (!nlh) {
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
data = NLMSG_DATA(nlh);
|
||||
data = nlmsg_data(nlh);
|
||||
|
||||
memcpy(data, msg, sizeof(*data) + msg->len);
|
||||
|
||||
NETLINK_CB(skb).dst_group = group;
|
||||
|
||||
return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
|
||||
|
||||
nlmsg_failure:
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cn_netlink_send);
|
||||
|
||||
@ -185,7 +185,8 @@ static void cn_rx_skb(struct sk_buff *__skb)
|
||||
* May sleep.
|
||||
*/
|
||||
int cn_add_callback(struct cb_id *id, const char *name,
|
||||
void (*callback)(struct cn_msg *, struct netlink_skb_parms *))
|
||||
void (*callback)(struct cn_msg *,
|
||||
struct netlink_skb_parms *))
|
||||
{
|
||||
int err;
|
||||
struct cn_dev *dev = &cdev;
|
||||
@ -251,15 +252,20 @@ static const struct file_operations cn_file_ops = {
|
||||
.release = single_release
|
||||
};
|
||||
|
||||
static struct cn_dev cdev = {
|
||||
.input = cn_rx_skb,
|
||||
};
|
||||
|
||||
static int __devinit cn_init(void)
|
||||
{
|
||||
struct cn_dev *dev = &cdev;
|
||||
|
||||
dev->input = cn_rx_skb;
|
||||
struct netlink_kernel_cfg cfg = {
|
||||
.groups = CN_NETLINK_USERS + 0xf,
|
||||
.input = dev->input,
|
||||
};
|
||||
|
||||
dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR,
|
||||
CN_NETLINK_USERS + 0xf,
|
||||
dev->input, NULL, THIS_MODULE);
|
||||
THIS_MODULE, &cfg);
|
||||
if (!dev->nls)
|
||||
return -EIO;
|
||||
|
||||
|
@ -19,6 +19,7 @@ config IEEE802154_FAKEHARD
|
||||
|
||||
This driver can also be built as a module. To do so say M here.
|
||||
The module will be called 'fakehard'.
|
||||
|
||||
config IEEE802154_FAKELB
|
||||
depends on IEEE802154_DRIVERS && MAC802154
|
||||
tristate "IEEE 802.15.4 loopback driver"
|
||||
@ -28,3 +29,8 @@ config IEEE802154_FAKELB
|
||||
|
||||
This driver can also be built as a module. To do so say M here.
|
||||
The module will be called 'fakelb'.
|
||||
|
||||
config IEEE802154_AT86RF230
|
||||
depends on IEEE802154_DRIVERS && MAC802154
|
||||
tristate "AT86RF230/231 transceiver driver"
|
||||
depends on SPI
|
||||
|
@ -1,2 +1,3 @@
|
||||
obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o
|
||||
obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
|
||||
obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o
|
||||
|
968
drivers/ieee802154/at86rf230.c
Normal file
968
drivers/ieee802154/at86rf230.c
Normal file
@ -0,0 +1,968 @@
|
||||
/*
|
||||
* AT86RF230/RF231 driver
|
||||
*
|
||||
* Copyright (C) 2009-2012 Siemens AG
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by:
|
||||
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
|
||||
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/at86rf230.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/mac802154.h>
|
||||
#include <net/wpan-phy.h>
|
||||
|
||||
struct at86rf230_local {
|
||||
struct spi_device *spi;
|
||||
int rstn, slp_tr, dig2;
|
||||
|
||||
u8 part;
|
||||
u8 vers;
|
||||
|
||||
u8 buf[2];
|
||||
struct mutex bmux;
|
||||
|
||||
struct work_struct irqwork;
|
||||
struct completion tx_complete;
|
||||
|
||||
struct ieee802154_dev *dev;
|
||||
|
||||
spinlock_t lock;
|
||||
bool irq_disabled;
|
||||
bool is_tx;
|
||||
};
|
||||
|
||||
#define RG_TRX_STATUS (0x01)
|
||||
#define SR_TRX_STATUS 0x01, 0x1f, 0
|
||||
#define SR_RESERVED_01_3 0x01, 0x20, 5
|
||||
#define SR_CCA_STATUS 0x01, 0x40, 6
|
||||
#define SR_CCA_DONE 0x01, 0x80, 7
|
||||
#define RG_TRX_STATE (0x02)
|
||||
#define SR_TRX_CMD 0x02, 0x1f, 0
|
||||
#define SR_TRAC_STATUS 0x02, 0xe0, 5
|
||||
#define RG_TRX_CTRL_0 (0x03)
|
||||
#define SR_CLKM_CTRL 0x03, 0x07, 0
|
||||
#define SR_CLKM_SHA_SEL 0x03, 0x08, 3
|
||||
#define SR_PAD_IO_CLKM 0x03, 0x30, 4
|
||||
#define SR_PAD_IO 0x03, 0xc0, 6
|
||||
#define RG_TRX_CTRL_1 (0x04)
|
||||
#define SR_IRQ_POLARITY 0x04, 0x01, 0
|
||||
#define SR_IRQ_MASK_MODE 0x04, 0x02, 1
|
||||
#define SR_SPI_CMD_MODE 0x04, 0x0c, 2
|
||||
#define SR_RX_BL_CTRL 0x04, 0x10, 4
|
||||
#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5
|
||||
#define SR_IRQ_2_EXT_EN 0x04, 0x40, 6
|
||||
#define SR_PA_EXT_EN 0x04, 0x80, 7
|
||||
#define RG_PHY_TX_PWR (0x05)
|
||||
#define SR_TX_PWR 0x05, 0x0f, 0
|
||||
#define SR_PA_LT 0x05, 0x30, 4
|
||||
#define SR_PA_BUF_LT 0x05, 0xc0, 6
|
||||
#define RG_PHY_RSSI (0x06)
|
||||
#define SR_RSSI 0x06, 0x1f, 0
|
||||
#define SR_RND_VALUE 0x06, 0x60, 5
|
||||
#define SR_RX_CRC_VALID 0x06, 0x80, 7
|
||||
#define RG_PHY_ED_LEVEL (0x07)
|
||||
#define SR_ED_LEVEL 0x07, 0xff, 0
|
||||
#define RG_PHY_CC_CCA (0x08)
|
||||
#define SR_CHANNEL 0x08, 0x1f, 0
|
||||
#define SR_CCA_MODE 0x08, 0x60, 5
|
||||
#define SR_CCA_REQUEST 0x08, 0x80, 7
|
||||
#define RG_CCA_THRES (0x09)
|
||||
#define SR_CCA_ED_THRES 0x09, 0x0f, 0
|
||||
#define SR_RESERVED_09_1 0x09, 0xf0, 4
|
||||
#define RG_RX_CTRL (0x0a)
|
||||
#define SR_PDT_THRES 0x0a, 0x0f, 0
|
||||
#define SR_RESERVED_0a_1 0x0a, 0xf0, 4
|
||||
#define RG_SFD_VALUE (0x0b)
|
||||
#define SR_SFD_VALUE 0x0b, 0xff, 0
|
||||
#define RG_TRX_CTRL_2 (0x0c)
|
||||
#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0
|
||||
#define SR_RESERVED_0c_2 0x0c, 0x7c, 2
|
||||
#define SR_RX_SAFE_MODE 0x0c, 0x80, 7
|
||||
#define RG_ANT_DIV (0x0d)
|
||||
#define SR_ANT_CTRL 0x0d, 0x03, 0
|
||||
#define SR_ANT_EXT_SW_EN 0x0d, 0x04, 2
|
||||
#define SR_ANT_DIV_EN 0x0d, 0x08, 3
|
||||
#define SR_RESERVED_0d_2 0x0d, 0x70, 4
|
||||
#define SR_ANT_SEL 0x0d, 0x80, 7
|
||||
#define RG_IRQ_MASK (0x0e)
|
||||
#define SR_IRQ_MASK 0x0e, 0xff, 0
|
||||
#define RG_IRQ_STATUS (0x0f)
|
||||
#define SR_IRQ_0_PLL_LOCK 0x0f, 0x01, 0
|
||||
#define SR_IRQ_1_PLL_UNLOCK 0x0f, 0x02, 1
|
||||
#define SR_IRQ_2_RX_START 0x0f, 0x04, 2
|
||||
#define SR_IRQ_3_TRX_END 0x0f, 0x08, 3
|
||||
#define SR_IRQ_4_CCA_ED_DONE 0x0f, 0x10, 4
|
||||
#define SR_IRQ_5_AMI 0x0f, 0x20, 5
|
||||
#define SR_IRQ_6_TRX_UR 0x0f, 0x40, 6
|
||||
#define SR_IRQ_7_BAT_LOW 0x0f, 0x80, 7
|
||||
#define RG_VREG_CTRL (0x10)
|
||||
#define SR_RESERVED_10_6 0x10, 0x03, 0
|
||||
#define SR_DVDD_OK 0x10, 0x04, 2
|
||||
#define SR_DVREG_EXT 0x10, 0x08, 3
|
||||
#define SR_RESERVED_10_3 0x10, 0x30, 4
|
||||
#define SR_AVDD_OK 0x10, 0x40, 6
|
||||
#define SR_AVREG_EXT 0x10, 0x80, 7
|
||||
#define RG_BATMON (0x11)
|
||||
#define SR_BATMON_VTH 0x11, 0x0f, 0
|
||||
#define SR_BATMON_HR 0x11, 0x10, 4
|
||||
#define SR_BATMON_OK 0x11, 0x20, 5
|
||||
#define SR_RESERVED_11_1 0x11, 0xc0, 6
|
||||
#define RG_XOSC_CTRL (0x12)
|
||||
#define SR_XTAL_TRIM 0x12, 0x0f, 0
|
||||
#define SR_XTAL_MODE 0x12, 0xf0, 4
|
||||
#define RG_RX_SYN (0x15)
|
||||
#define SR_RX_PDT_LEVEL 0x15, 0x0f, 0
|
||||
#define SR_RESERVED_15_2 0x15, 0x70, 4
|
||||
#define SR_RX_PDT_DIS 0x15, 0x80, 7
|
||||
#define RG_XAH_CTRL_1 (0x17)
|
||||
#define SR_RESERVED_17_8 0x17, 0x01, 0
|
||||
#define SR_AACK_PROM_MODE 0x17, 0x02, 1
|
||||
#define SR_AACK_ACK_TIME 0x17, 0x04, 2
|
||||
#define SR_RESERVED_17_5 0x17, 0x08, 3
|
||||
#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4
|
||||
#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5
|
||||
#define SR_RESERVED_17_2 0x17, 0x40, 6
|
||||
#define SR_RESERVED_17_1 0x17, 0x80, 7
|
||||
#define RG_FTN_CTRL (0x18)
|
||||
#define SR_RESERVED_18_2 0x18, 0x7f, 0
|
||||
#define SR_FTN_START 0x18, 0x80, 7
|
||||
#define RG_PLL_CF (0x1a)
|
||||
#define SR_RESERVED_1a_2 0x1a, 0x7f, 0
|
||||
#define SR_PLL_CF_START 0x1a, 0x80, 7
|
||||
#define RG_PLL_DCU (0x1b)
|
||||
#define SR_RESERVED_1b_3 0x1b, 0x3f, 0
|
||||
#define SR_RESERVED_1b_2 0x1b, 0x40, 6
|
||||
#define SR_PLL_DCU_START 0x1b, 0x80, 7
|
||||
#define RG_PART_NUM (0x1c)
|
||||
#define SR_PART_NUM 0x1c, 0xff, 0
|
||||
#define RG_VERSION_NUM (0x1d)
|
||||
#define SR_VERSION_NUM 0x1d, 0xff, 0
|
||||
#define RG_MAN_ID_0 (0x1e)
|
||||
#define SR_MAN_ID_0 0x1e, 0xff, 0
|
||||
#define RG_MAN_ID_1 (0x1f)
|
||||
#define SR_MAN_ID_1 0x1f, 0xff, 0
|
||||
#define RG_SHORT_ADDR_0 (0x20)
|
||||
#define SR_SHORT_ADDR_0 0x20, 0xff, 0
|
||||
#define RG_SHORT_ADDR_1 (0x21)
|
||||
#define SR_SHORT_ADDR_1 0x21, 0xff, 0
|
||||
#define RG_PAN_ID_0 (0x22)
|
||||
#define SR_PAN_ID_0 0x22, 0xff, 0
|
||||
#define RG_PAN_ID_1 (0x23)
|
||||
#define SR_PAN_ID_1 0x23, 0xff, 0
|
||||
#define RG_IEEE_ADDR_0 (0x24)
|
||||
#define SR_IEEE_ADDR_0 0x24, 0xff, 0
|
||||
#define RG_IEEE_ADDR_1 (0x25)
|
||||
#define SR_IEEE_ADDR_1 0x25, 0xff, 0
|
||||
#define RG_IEEE_ADDR_2 (0x26)
|
||||
#define SR_IEEE_ADDR_2 0x26, 0xff, 0
|
||||
#define RG_IEEE_ADDR_3 (0x27)
|
||||
#define SR_IEEE_ADDR_3 0x27, 0xff, 0
|
||||
#define RG_IEEE_ADDR_4 (0x28)
|
||||
#define SR_IEEE_ADDR_4 0x28, 0xff, 0
|
||||
#define RG_IEEE_ADDR_5 (0x29)
|
||||
#define SR_IEEE_ADDR_5 0x29, 0xff, 0
|
||||
#define RG_IEEE_ADDR_6 (0x2a)
|
||||
#define SR_IEEE_ADDR_6 0x2a, 0xff, 0
|
||||
#define RG_IEEE_ADDR_7 (0x2b)
|
||||
#define SR_IEEE_ADDR_7 0x2b, 0xff, 0
|
||||
#define RG_XAH_CTRL_0 (0x2c)
|
||||
#define SR_SLOTTED_OPERATION 0x2c, 0x01, 0
|
||||
#define SR_MAX_CSMA_RETRIES 0x2c, 0x0e, 1
|
||||
#define SR_MAX_FRAME_RETRIES 0x2c, 0xf0, 4
|
||||
#define RG_CSMA_SEED_0 (0x2d)
|
||||
#define SR_CSMA_SEED_0 0x2d, 0xff, 0
|
||||
#define RG_CSMA_SEED_1 (0x2e)
|
||||
#define SR_CSMA_SEED_1 0x2e, 0x07, 0
|
||||
#define SR_AACK_I_AM_COORD 0x2e, 0x08, 3
|
||||
#define SR_AACK_DIS_ACK 0x2e, 0x10, 4
|
||||
#define SR_AACK_SET_PD 0x2e, 0x20, 5
|
||||
#define SR_AACK_FVN_MODE 0x2e, 0xc0, 6
|
||||
#define RG_CSMA_BE (0x2f)
|
||||
#define SR_MIN_BE 0x2f, 0x0f, 0
|
||||
#define SR_MAX_BE 0x2f, 0xf0, 4
|
||||
|
||||
#define CMD_REG 0x80
|
||||
#define CMD_REG_MASK 0x3f
|
||||
#define CMD_WRITE 0x40
|
||||
#define CMD_FB 0x20
|
||||
|
||||
#define IRQ_BAT_LOW (1 << 7)
|
||||
#define IRQ_TRX_UR (1 << 6)
|
||||
#define IRQ_AMI (1 << 5)
|
||||
#define IRQ_CCA_ED (1 << 4)
|
||||
#define IRQ_TRX_END (1 << 3)
|
||||
#define IRQ_RX_START (1 << 2)
|
||||
#define IRQ_PLL_UNL (1 << 1)
|
||||
#define IRQ_PLL_LOCK (1 << 0)
|
||||
|
||||
#define STATE_P_ON 0x00 /* BUSY */
|
||||
#define STATE_BUSY_RX 0x01
|
||||
#define STATE_BUSY_TX 0x02
|
||||
#define STATE_FORCE_TRX_OFF 0x03
|
||||
#define STATE_FORCE_TX_ON 0x04 /* IDLE */
|
||||
/* 0x05 */ /* INVALID_PARAMETER */
|
||||
#define STATE_RX_ON 0x06
|
||||
/* 0x07 */ /* SUCCESS */
|
||||
#define STATE_TRX_OFF 0x08
|
||||
#define STATE_TX_ON 0x09
|
||||
/* 0x0a - 0x0e */ /* 0x0a - UNSUPPORTED_ATTRIBUTE */
|
||||
#define STATE_SLEEP 0x0F
|
||||
#define STATE_BUSY_RX_AACK 0x11
|
||||
#define STATE_BUSY_TX_ARET 0x12
|
||||
#define STATE_BUSY_RX_AACK_ON 0x16
|
||||
#define STATE_BUSY_TX_ARET_ON 0x19
|
||||
#define STATE_RX_ON_NOCLK 0x1C
|
||||
#define STATE_RX_AACK_ON_NOCLK 0x1D
|
||||
#define STATE_BUSY_RX_AACK_NOCLK 0x1E
|
||||
#define STATE_TRANSITION_IN_PROGRESS 0x1F
|
||||
|
||||
static int
|
||||
__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data)
|
||||
{
|
||||
u8 *buf = lp->buf;
|
||||
int status;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer = {
|
||||
.len = 2,
|
||||
.tx_buf = buf,
|
||||
};
|
||||
|
||||
buf[0] = (addr & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
|
||||
buf[1] = data;
|
||||
dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
|
||||
dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
|
||||
status = spi_sync(lp->spi, &msg);
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
if (msg.status)
|
||||
status = msg.status;
|
||||
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
|
||||
dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
__at86rf230_read_subreg(struct at86rf230_local *lp,
|
||||
u8 addr, u8 mask, int shift, u8 *data)
|
||||
{
|
||||
u8 *buf = lp->buf;
|
||||
int status;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer = {
|
||||
.len = 2,
|
||||
.tx_buf = buf,
|
||||
.rx_buf = buf,
|
||||
};
|
||||
|
||||
buf[0] = (addr & CMD_REG_MASK) | CMD_REG;
|
||||
buf[1] = 0xff;
|
||||
dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
|
||||
status = spi_sync(lp->spi, &msg);
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
if (msg.status)
|
||||
status = msg.status;
|
||||
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
|
||||
dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
|
||||
|
||||
if (status == 0)
|
||||
*data = buf[1];
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_read_subreg(struct at86rf230_local *lp,
|
||||
u8 addr, u8 mask, int shift, u8 *data)
|
||||
{
|
||||
int status;
|
||||
|
||||
mutex_lock(&lp->bmux);
|
||||
status = __at86rf230_read_subreg(lp, addr, mask, shift, data);
|
||||
mutex_unlock(&lp->bmux);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_write_subreg(struct at86rf230_local *lp,
|
||||
u8 addr, u8 mask, int shift, u8 data)
|
||||
{
|
||||
int status;
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&lp->bmux);
|
||||
status = __at86rf230_read_subreg(lp, addr, 0xff, 0, &val);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
val &= ~mask;
|
||||
val |= (data << shift) & mask;
|
||||
|
||||
status = __at86rf230_write(lp, addr, val);
|
||||
out:
|
||||
mutex_unlock(&lp->bmux);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_write_fbuf(struct at86rf230_local *lp, u8 *data, u8 len)
|
||||
{
|
||||
u8 *buf = lp->buf;
|
||||
int status;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer_head = {
|
||||
.len = 2,
|
||||
.tx_buf = buf,
|
||||
|
||||
};
|
||||
struct spi_transfer xfer_buf = {
|
||||
.len = len,
|
||||
.tx_buf = data,
|
||||
};
|
||||
|
||||
mutex_lock(&lp->bmux);
|
||||
buf[0] = CMD_WRITE | CMD_FB;
|
||||
buf[1] = len + 2; /* 2 bytes for CRC that isn't written */
|
||||
|
||||
dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
|
||||
dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer_head, &msg);
|
||||
spi_message_add_tail(&xfer_buf, &msg);
|
||||
|
||||
status = spi_sync(lp->spi, &msg);
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
if (msg.status)
|
||||
status = msg.status;
|
||||
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
|
||||
dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
|
||||
|
||||
mutex_unlock(&lp->bmux);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_read_fbuf(struct at86rf230_local *lp, u8 *data, u8 *len, u8 *lqi)
|
||||
{
|
||||
u8 *buf = lp->buf;
|
||||
int status;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer_head = {
|
||||
.len = 2,
|
||||
.tx_buf = buf,
|
||||
.rx_buf = buf,
|
||||
};
|
||||
struct spi_transfer xfer_head1 = {
|
||||
.len = 2,
|
||||
.tx_buf = buf,
|
||||
.rx_buf = buf,
|
||||
};
|
||||
struct spi_transfer xfer_buf = {
|
||||
.len = 0,
|
||||
.rx_buf = data,
|
||||
};
|
||||
|
||||
mutex_lock(&lp->bmux);
|
||||
|
||||
buf[0] = CMD_FB;
|
||||
buf[1] = 0x00;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer_head, &msg);
|
||||
|
||||
status = spi_sync(lp->spi, &msg);
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
|
||||
xfer_buf.len = *(buf + 1) + 1;
|
||||
*len = buf[1];
|
||||
|
||||
buf[0] = CMD_FB;
|
||||
buf[1] = 0x00;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer_head1, &msg);
|
||||
spi_message_add_tail(&xfer_buf, &msg);
|
||||
|
||||
status = spi_sync(lp->spi, &msg);
|
||||
|
||||
if (msg.status)
|
||||
status = msg.status;
|
||||
|
||||
dev_vdbg(&lp->spi->dev, "status = %d\n", status);
|
||||
dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
|
||||
dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
|
||||
|
||||
if (status) {
|
||||
if (lqi && (*len > lp->buf[1]))
|
||||
*lqi = data[lp->buf[1]];
|
||||
}
|
||||
mutex_unlock(&lp->bmux);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_ed(struct ieee802154_dev *dev, u8 *level)
|
||||
{
|
||||
might_sleep();
|
||||
BUG_ON(!level);
|
||||
*level = 0xbe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_state(struct ieee802154_dev *dev, int state)
|
||||
{
|
||||
struct at86rf230_local *lp = dev->priv;
|
||||
int rc;
|
||||
u8 val;
|
||||
u8 desired_status;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (state == STATE_FORCE_TX_ON)
|
||||
desired_status = STATE_TX_ON;
|
||||
else if (state == STATE_FORCE_TRX_OFF)
|
||||
desired_status = STATE_TRX_OFF;
|
||||
else
|
||||
desired_status = state;
|
||||
|
||||
do {
|
||||
rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val);
|
||||
if (rc)
|
||||
goto err;
|
||||
} while (val == STATE_TRANSITION_IN_PROGRESS);
|
||||
|
||||
if (val == desired_status)
|
||||
return 0;
|
||||
|
||||
/* state is equal to phy states */
|
||||
rc = at86rf230_write_subreg(lp, SR_TRX_CMD, state);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
do {
|
||||
rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val);
|
||||
if (rc)
|
||||
goto err;
|
||||
} while (val == STATE_TRANSITION_IN_PROGRESS);
|
||||
|
||||
|
||||
if (val == desired_status)
|
||||
return 0;
|
||||
|
||||
pr_err("unexpected state change: %d, asked for %d\n", val, state);
|
||||
return -EBUSY;
|
||||
|
||||
err:
|
||||
pr_err("error: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_start(struct ieee802154_dev *dev)
|
||||
{
|
||||
struct at86rf230_local *lp = dev->priv;
|
||||
u8 rc;
|
||||
|
||||
rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return at86rf230_state(dev, STATE_RX_ON);
|
||||
}
|
||||
|
||||
static void
|
||||
at86rf230_stop(struct ieee802154_dev *dev)
|
||||
{
|
||||
at86rf230_state(dev, STATE_FORCE_TRX_OFF);
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
|
||||
{
|
||||
struct at86rf230_local *lp = dev->priv;
|
||||
int rc;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (page != 0 || channel < 11 || channel > 26) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel);
|
||||
msleep(1); /* Wait for PLL */
|
||||
dev->phy->current_channel = channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct at86rf230_local *lp = dev->priv;
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&lp->lock);
|
||||
if (lp->irq_disabled) {
|
||||
spin_unlock(&lp->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
spin_unlock(&lp->lock);
|
||||
|
||||
might_sleep();
|
||||
|
||||
rc = at86rf230_state(dev, STATE_FORCE_TX_ON);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
lp->is_tx = 1;
|
||||
INIT_COMPLETION(lp->tx_complete);
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
rc = at86rf230_write_fbuf(lp, skb->data, skb->len);
|
||||
if (rc)
|
||||
goto err_rx;
|
||||
|
||||
rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX);
|
||||
if (rc)
|
||||
goto err_rx;
|
||||
|
||||
rc = wait_for_completion_interruptible(&lp->tx_complete);
|
||||
if (rc < 0)
|
||||
goto err_rx;
|
||||
|
||||
rc = at86rf230_start(dev);
|
||||
|
||||
return rc;
|
||||
|
||||
err_rx:
|
||||
at86rf230_start(dev);
|
||||
err:
|
||||
pr_err("error: %d\n", rc);
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
lp->is_tx = 0;
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int at86rf230_rx(struct at86rf230_local *lp)
|
||||
{
|
||||
u8 len = 128, lqi = 0;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(len, GFP_KERNEL);
|
||||
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
if (at86rf230_read_fbuf(lp, skb_put(skb, len), &len, &lqi))
|
||||
goto err;
|
||||
|
||||
if (len < 2)
|
||||
goto err;
|
||||
|
||||
skb_trim(skb, len - 2); /* We do not put CRC into the frame */
|
||||
|
||||
ieee802154_rx_irqsafe(lp->dev, skb, lqi);
|
||||
|
||||
dev_dbg(&lp->spi->dev, "READ_FBUF: %d %x\n", len, lqi);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
pr_debug("received frame is too small\n");
|
||||
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct ieee802154_ops at86rf230_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.xmit = at86rf230_xmit,
|
||||
.ed = at86rf230_ed,
|
||||
.set_channel = at86rf230_channel,
|
||||
.start = at86rf230_start,
|
||||
.stop = at86rf230_stop,
|
||||
};
|
||||
|
||||
static void at86rf230_irqwork(struct work_struct *work)
|
||||
{
|
||||
struct at86rf230_local *lp =
|
||||
container_of(work, struct at86rf230_local, irqwork);
|
||||
u8 status = 0, val;
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &val);
|
||||
status |= val;
|
||||
|
||||
status &= ~IRQ_PLL_LOCK; /* ignore */
|
||||
status &= ~IRQ_RX_START; /* ignore */
|
||||
status &= ~IRQ_AMI; /* ignore */
|
||||
status &= ~IRQ_TRX_UR; /* FIXME: possibly handle ???*/
|
||||
|
||||
if (status & IRQ_TRX_END) {
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
status &= ~IRQ_TRX_END;
|
||||
if (lp->is_tx) {
|
||||
lp->is_tx = 0;
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
complete(&lp->tx_complete);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
at86rf230_rx(lp);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
lp->irq_disabled = 0;
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
enable_irq(lp->spi->irq);
|
||||
}
|
||||
|
||||
static irqreturn_t at86rf230_isr(int irq, void *data)
|
||||
{
|
||||
struct at86rf230_local *lp = data;
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
|
||||
spin_lock(&lp->lock);
|
||||
lp->irq_disabled = 1;
|
||||
spin_unlock(&lp->lock);
|
||||
|
||||
schedule_work(&lp->irqwork);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static int at86rf230_hw_init(struct at86rf230_local *lp)
|
||||
{
|
||||
u8 status;
|
||||
int rc;
|
||||
|
||||
rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev_info(&lp->spi->dev, "Status: %02x\n", status);
|
||||
if (status == STATE_P_ON) {
|
||||
rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF);
|
||||
if (rc)
|
||||
return rc;
|
||||
msleep(1);
|
||||
rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
|
||||
if (rc)
|
||||
return rc;
|
||||
dev_info(&lp->spi->dev, "Status: %02x\n", status);
|
||||
}
|
||||
|
||||
rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, 0xff); /* IRQ_TRX_UR |
|
||||
* IRQ_CCA_ED |
|
||||
* IRQ_TRX_END |
|
||||
* IRQ_PLL_UNL |
|
||||
* IRQ_PLL_LOCK
|
||||
*/
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* CLKM changes are applied immediately */
|
||||
rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Turn CLKM Off */
|
||||
rc = at86rf230_write_subreg(lp, SR_CLKM_CTRL, 0x00);
|
||||
if (rc)
|
||||
return rc;
|
||||
/* Wait the next SLEEP cycle */
|
||||
msleep(100);
|
||||
|
||||
rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ON);
|
||||
if (rc)
|
||||
return rc;
|
||||
msleep(1);
|
||||
|
||||
rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
|
||||
if (rc)
|
||||
return rc;
|
||||
dev_info(&lp->spi->dev, "Status: %02x\n", status);
|
||||
|
||||
rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!status) {
|
||||
dev_err(&lp->spi->dev, "DVDD error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = at86rf230_read_subreg(lp, SR_AVDD_OK, &status);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!status) {
|
||||
dev_err(&lp->spi->dev, "AVDD error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at86rf230_suspend(struct spi_device *spi, pm_message_t message)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at86rf230_resume(struct spi_device *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at86rf230_fill_data(struct spi_device *spi)
|
||||
{
|
||||
struct at86rf230_local *lp = spi_get_drvdata(spi);
|
||||
struct at86rf230_platform_data *pdata = spi->dev.platform_data;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&spi->dev, "no platform_data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lp->rstn = pdata->rstn;
|
||||
lp->slp_tr = pdata->slp_tr;
|
||||
lp->dig2 = pdata->dig2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit at86rf230_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ieee802154_dev *dev;
|
||||
struct at86rf230_local *lp;
|
||||
u8 man_id_0, man_id_1;
|
||||
int rc;
|
||||
const char *chip;
|
||||
int supported = 0;
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_err(&spi->dev, "no IRQ specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
lp = dev->priv;
|
||||
lp->dev = dev;
|
||||
|
||||
lp->spi = spi;
|
||||
|
||||
dev->priv = lp;
|
||||
dev->parent = &spi->dev;
|
||||
dev->extra_tx_headroom = 0;
|
||||
/* We do support only 2.4 Ghz */
|
||||
dev->phy->channels_supported[0] = 0x7FFF800;
|
||||
dev->flags = IEEE802154_HW_OMIT_CKSUM;
|
||||
|
||||
mutex_init(&lp->bmux);
|
||||
INIT_WORK(&lp->irqwork, at86rf230_irqwork);
|
||||
spin_lock_init(&lp->lock);
|
||||
init_completion(&lp->tx_complete);
|
||||
|
||||
spi_set_drvdata(spi, lp);
|
||||
|
||||
rc = at86rf230_fill_data(spi);
|
||||
if (rc)
|
||||
goto err_fill;
|
||||
|
||||
rc = gpio_request(lp->rstn, "rstn");
|
||||
if (rc)
|
||||
goto err_rstn;
|
||||
|
||||
if (gpio_is_valid(lp->slp_tr)) {
|
||||
rc = gpio_request(lp->slp_tr, "slp_tr");
|
||||
if (rc)
|
||||
goto err_slp_tr;
|
||||
}
|
||||
|
||||
rc = gpio_direction_output(lp->rstn, 1);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
|
||||
if (gpio_is_valid(lp->slp_tr)) {
|
||||
rc = gpio_direction_output(lp->slp_tr, 0);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
msleep(1);
|
||||
gpio_set_value(lp->rstn, 0);
|
||||
msleep(1);
|
||||
gpio_set_value(lp->rstn, 1);
|
||||
msleep(1);
|
||||
|
||||
rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
|
||||
if (man_id_1 != 0x00 || man_id_0 != 0x1f) {
|
||||
dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
|
||||
man_id_1, man_id_0);
|
||||
rc = -EINVAL;
|
||||
goto err_gpio_dir;
|
||||
}
|
||||
|
||||
rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
|
||||
rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
|
||||
switch (lp->part) {
|
||||
case 2:
|
||||
chip = "at86rf230";
|
||||
/* supported = 1; FIXME: should be easy to support; */
|
||||
break;
|
||||
case 3:
|
||||
chip = "at86rf231";
|
||||
supported = 1;
|
||||
break;
|
||||
default:
|
||||
chip = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers);
|
||||
if (!supported) {
|
||||
rc = -ENOTSUPP;
|
||||
goto err_gpio_dir;
|
||||
}
|
||||
|
||||
rc = at86rf230_hw_init(lp);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
|
||||
rc = request_irq(spi->irq, at86rf230_isr, IRQF_SHARED,
|
||||
dev_name(&spi->dev), lp);
|
||||
if (rc)
|
||||
goto err_gpio_dir;
|
||||
|
||||
rc = ieee802154_register_device(lp->dev);
|
||||
if (rc)
|
||||
goto err_irq;
|
||||
|
||||
return rc;
|
||||
|
||||
ieee802154_unregister_device(lp->dev);
|
||||
err_irq:
|
||||
free_irq(spi->irq, lp);
|
||||
flush_work(&lp->irqwork);
|
||||
err_gpio_dir:
|
||||
if (gpio_is_valid(lp->slp_tr))
|
||||
gpio_free(lp->slp_tr);
|
||||
err_slp_tr:
|
||||
gpio_free(lp->rstn);
|
||||
err_rstn:
|
||||
err_fill:
|
||||
spi_set_drvdata(spi, NULL);
|
||||
mutex_destroy(&lp->bmux);
|
||||
ieee802154_free_device(lp->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devexit at86rf230_remove(struct spi_device *spi)
|
||||
{
|
||||
struct at86rf230_local *lp = spi_get_drvdata(spi);
|
||||
|
||||
ieee802154_unregister_device(lp->dev);
|
||||
|
||||
free_irq(spi->irq, lp);
|
||||
flush_work(&lp->irqwork);
|
||||
|
||||
if (gpio_is_valid(lp->slp_tr))
|
||||
gpio_free(lp->slp_tr);
|
||||
gpio_free(lp->rstn);
|
||||
|
||||
spi_set_drvdata(spi, NULL);
|
||||
mutex_destroy(&lp->bmux);
|
||||
ieee802154_free_device(lp->dev);
|
||||
|
||||
dev_dbg(&spi->dev, "unregistered at86rf230\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver at86rf230_driver = {
|
||||
.driver = {
|
||||
.name = "at86rf230",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = at86rf230_probe,
|
||||
.remove = __devexit_p(at86rf230_remove),
|
||||
.suspend = at86rf230_suspend,
|
||||
.resume = at86rf230_resume,
|
||||
};
|
||||
|
||||
static int __init at86rf230_init(void)
|
||||
{
|
||||
return spi_register_driver(&at86rf230_driver);
|
||||
}
|
||||
module_init(at86rf230_init);
|
||||
|
||||
static void __exit at86rf230_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&at86rf230_driver);
|
||||
}
|
||||
module_exit(at86rf230_exit);
|
||||
|
||||
MODULE_DESCRIPTION("AT86RF230 Transceiver Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -108,12 +108,14 @@ void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq,
|
||||
unsigned char *prev_tail;
|
||||
|
||||
prev_tail = skb_tail_pointer(skb);
|
||||
*nlh = NLMSG_NEW(skb, 0, seq, RDMA_NL_GET_TYPE(client, op),
|
||||
len, NLM_F_MULTI);
|
||||
*nlh = nlmsg_put(skb, 0, seq, RDMA_NL_GET_TYPE(client, op),
|
||||
len, NLM_F_MULTI);
|
||||
if (!*nlh)
|
||||
goto out_nlmsg_trim;
|
||||
(*nlh)->nlmsg_len = skb_tail_pointer(skb) - prev_tail;
|
||||
return NLMSG_DATA(*nlh);
|
||||
return nlmsg_data(*nlh);
|
||||
|
||||
nlmsg_failure:
|
||||
out_nlmsg_trim:
|
||||
nlmsg_trim(skb, prev_tail);
|
||||
return NULL;
|
||||
}
|
||||
@ -171,8 +173,11 @@ static void ibnl_rcv(struct sk_buff *skb)
|
||||
|
||||
int __init ibnl_init(void)
|
||||
{
|
||||
nls = netlink_kernel_create(&init_net, NETLINK_RDMA, 0, ibnl_rcv,
|
||||
NULL, THIS_MODULE);
|
||||
struct netlink_kernel_cfg cfg = {
|
||||
.input = ibnl_rcv,
|
||||
};
|
||||
|
||||
nls = netlink_kernel_create(&init_net, NETLINK_RDMA, THIS_MODULE, &cfg);
|
||||
if (!nls) {
|
||||
pr_warn("Failed to create netlink socket\n");
|
||||
return -ENOMEM;
|
||||
|
@ -1374,7 +1374,7 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
||||
goto reject;
|
||||
}
|
||||
dst = &rt->dst;
|
||||
l2t = t3_l2t_get(tdev, dst, NULL);
|
||||
l2t = t3_l2t_get(tdev, dst, NULL, &req->peer_ip);
|
||||
if (!l2t) {
|
||||
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
||||
__func__);
|
||||
@ -1942,7 +1942,8 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
||||
goto fail3;
|
||||
}
|
||||
ep->dst = &rt->dst;
|
||||
ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst, NULL);
|
||||
ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst, NULL,
|
||||
&cm_id->remote_addr.sin_addr.s_addr);
|
||||
if (!ep->l2t) {
|
||||
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
||||
err = -ENOMEM;
|
||||
|
@ -718,26 +718,53 @@ int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct mlx4_ib_steering {
|
||||
struct list_head list;
|
||||
u64 reg_id;
|
||||
union ib_gid gid;
|
||||
};
|
||||
|
||||
static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
|
||||
{
|
||||
int err;
|
||||
struct mlx4_ib_dev *mdev = to_mdev(ibqp->device);
|
||||
struct mlx4_ib_qp *mqp = to_mqp(ibqp);
|
||||
u64 reg_id;
|
||||
struct mlx4_ib_steering *ib_steering = NULL;
|
||||
|
||||
err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw,
|
||||
!!(mqp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK),
|
||||
MLX4_PROT_IB_IPV6);
|
||||
if (mdev->dev->caps.steering_mode ==
|
||||
MLX4_STEERING_MODE_DEVICE_MANAGED) {
|
||||
ib_steering = kmalloc(sizeof(*ib_steering), GFP_KERNEL);
|
||||
if (!ib_steering)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, mqp->port,
|
||||
!!(mqp->flags &
|
||||
MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK),
|
||||
MLX4_PROT_IB_IPV6, ®_id);
|
||||
if (err)
|
||||
return err;
|
||||
goto err_malloc;
|
||||
|
||||
err = add_gid_entry(ibqp, gid);
|
||||
if (err)
|
||||
goto err_add;
|
||||
|
||||
if (ib_steering) {
|
||||
memcpy(ib_steering->gid.raw, gid->raw, 16);
|
||||
ib_steering->reg_id = reg_id;
|
||||
mutex_lock(&mqp->mutex);
|
||||
list_add(&ib_steering->list, &mqp->steering_rules);
|
||||
mutex_unlock(&mqp->mutex);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_add:
|
||||
mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6);
|
||||
mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw,
|
||||
MLX4_PROT_IB_IPV6, reg_id);
|
||||
err_malloc:
|
||||
kfree(ib_steering);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -765,9 +792,30 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
|
||||
u8 mac[6];
|
||||
struct net_device *ndev;
|
||||
struct mlx4_ib_gid_entry *ge;
|
||||
u64 reg_id = 0;
|
||||
|
||||
err = mlx4_multicast_detach(mdev->dev,
|
||||
&mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6);
|
||||
if (mdev->dev->caps.steering_mode ==
|
||||
MLX4_STEERING_MODE_DEVICE_MANAGED) {
|
||||
struct mlx4_ib_steering *ib_steering;
|
||||
|
||||
mutex_lock(&mqp->mutex);
|
||||
list_for_each_entry(ib_steering, &mqp->steering_rules, list) {
|
||||
if (!memcmp(ib_steering->gid.raw, gid->raw, 16)) {
|
||||
list_del(&ib_steering->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&mqp->mutex);
|
||||
if (&ib_steering->list == &mqp->steering_rules) {
|
||||
pr_err("Couldn't find reg_id for mgid. Steering rule is left attached\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
reg_id = ib_steering->reg_id;
|
||||
kfree(ib_steering);
|
||||
}
|
||||
|
||||
err = mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw,
|
||||
MLX4_PROT_IB_IPV6, reg_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1111,7 +1159,8 @@ static void mlx4_ib_alloc_eqs(struct mlx4_dev *dev, struct mlx4_ib_dev *ibdev)
|
||||
sprintf(name, "mlx4-ib-%d-%d@%s",
|
||||
i, j, dev->pdev->bus->name);
|
||||
/* Set IRQ for specific name (per ring) */
|
||||
if (mlx4_assign_eq(dev, name, &ibdev->eq_table[eq])) {
|
||||
if (mlx4_assign_eq(dev, name, NULL,
|
||||
&ibdev->eq_table[eq])) {
|
||||
/* Use legacy (same as mlx4_en driver) */
|
||||
pr_warn("Can't allocate EQ %d; reverting to legacy\n", eq);
|
||||
ibdev->eq_table[eq] =
|
||||
|
@ -163,6 +163,7 @@ struct mlx4_ib_qp {
|
||||
u8 state;
|
||||
int mlx_type;
|
||||
struct list_head gid_list;
|
||||
struct list_head steering_rules;
|
||||
};
|
||||
|
||||
struct mlx4_ib_srq {
|
||||
|
@ -495,6 +495,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
|
||||
spin_lock_init(&qp->sq.lock);
|
||||
spin_lock_init(&qp->rq.lock);
|
||||
INIT_LIST_HEAD(&qp->gid_list);
|
||||
INIT_LIST_HEAD(&qp->steering_rules);
|
||||
|
||||
qp->state = IB_QPS_RESET;
|
||||
if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
|
||||
|
@ -1397,7 +1397,7 @@ void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb,
|
||||
int e = skb_queue_empty(&priv->cm.skb_queue);
|
||||
|
||||
if (skb_dst(skb))
|
||||
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
|
||||
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
|
||||
|
||||
skb_queue_tail(&priv->cm.skb_queue, skb);
|
||||
if (e)
|
||||
|
@ -715,7 +715,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
rcu_read_lock();
|
||||
if (likely(skb_dst(skb))) {
|
||||
n = dst_get_neighbour_noref(skb_dst(skb));
|
||||
n = dst_neigh_lookup_skb(skb_dst(skb), skb);
|
||||
if (!n) {
|
||||
++dev->stats.tx_dropped;
|
||||
dev_kfree_skb_any(skb);
|
||||
@ -797,6 +797,8 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
if (n)
|
||||
neigh_release(n);
|
||||
rcu_read_unlock();
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
@ -658,9 +658,15 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
|
||||
void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct ipoib_mcast *mcast;
|
||||
struct neighbour *n;
|
||||
unsigned long flags;
|
||||
|
||||
n = NULL;
|
||||
if (dst)
|
||||
n = dst_neigh_lookup_skb(dst, skb);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags) ||
|
||||
@ -715,29 +721,28 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
|
||||
|
||||
out:
|
||||
if (mcast && mcast->ah) {
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct neighbour *n = NULL;
|
||||
if (n) {
|
||||
if (!*to_ipoib_neigh(n)) {
|
||||
struct ipoib_neigh *neigh;
|
||||
|
||||
rcu_read_lock();
|
||||
if (dst)
|
||||
n = dst_get_neighbour_noref(dst);
|
||||
if (n && !*to_ipoib_neigh(n)) {
|
||||
struct ipoib_neigh *neigh = ipoib_neigh_alloc(n,
|
||||
skb->dev);
|
||||
|
||||
if (neigh) {
|
||||
kref_get(&mcast->ah->ref);
|
||||
neigh->ah = mcast->ah;
|
||||
list_add_tail(&neigh->list, &mcast->neigh_list);
|
||||
neigh = ipoib_neigh_alloc(n, skb->dev);
|
||||
if (neigh) {
|
||||
kref_get(&mcast->ah->ref);
|
||||
neigh->ah = mcast->ah;
|
||||
list_add_tail(&neigh->list,
|
||||
&mcast->neigh_list);
|
||||
}
|
||||
}
|
||||
neigh_release(n);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
|
||||
return;
|
||||
}
|
||||
|
||||
unlock:
|
||||
if (n)
|
||||
neigh_release(n);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
|
@ -288,6 +288,7 @@ static inline void dump_rawmsg(enum debuglevel level, const char *tag,
|
||||
* format CAPI IE as string
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
static const char *format_ie(const char *ie)
|
||||
{
|
||||
static char result[3 * MAX_FMT_IE_LEN];
|
||||
@ -313,6 +314,7 @@ static const char *format_ie(const char *ie)
|
||||
*--pout = 0;
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* emit DATA_B3_CONF message
|
||||
|
@ -2084,13 +2084,21 @@ hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
/* create the control pipes needed for register access */
|
||||
hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0);
|
||||
hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0);
|
||||
hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
|
||||
driver_info =
|
||||
(struct hfcsusb_vdata *)hfcsusb_idtab[vend_idx].driver_info;
|
||||
printk(KERN_DEBUG "%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n",
|
||||
hw->name, __func__, driver_info->vend_name,
|
||||
conf_str[small_match], ifnum, alt_used);
|
||||
driver_info = (struct hfcsusb_vdata *)
|
||||
hfcsusb_idtab[vend_idx].driver_info;
|
||||
|
||||
hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!hw->ctrl_urb) {
|
||||
pr_warn("%s: No memory for control urb\n",
|
||||
driver_info->vend_name);
|
||||
kfree(hw);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n",
|
||||
hw->name, __func__, driver_info->vend_name,
|
||||
conf_str[small_match], ifnum, alt_used);
|
||||
|
||||
if (setup_instance(hw, dev->dev.parent))
|
||||
return -EIO;
|
||||
|
@ -1483,13 +1483,21 @@ hfc_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
usb_rcvctrlpipe(context->dev, 0);
|
||||
context->ctrl_out_pipe =
|
||||
usb_sndctrlpipe(context->dev, 0);
|
||||
|
||||
driver_info = (hfcsusb_vdata *)
|
||||
hfcusb_idtab[vend_idx].driver_info;
|
||||
|
||||
context->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
|
||||
driver_info =
|
||||
(hfcsusb_vdata *) hfcusb_idtab[vend_idx].
|
||||
driver_info;
|
||||
printk(KERN_INFO "HFC-S USB: detected \"%s\"\n",
|
||||
driver_info->vend_name);
|
||||
if (!context->ctrl_urb) {
|
||||
pr_warn("%s: No memory for control urb\n",
|
||||
driver_info->vend_name);
|
||||
kfree(context);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("HFC-S USB: detected \"%s\"\n",
|
||||
driver_info->vend_name);
|
||||
|
||||
DBG(HFCUSB_DBG_INIT,
|
||||
"HFC-S USB: Endpoint-Config: %s (if=%d alt=%d), E-Channel(%d)",
|
||||
|
@ -231,6 +231,11 @@ setup_isurf(struct IsdnCard *card)
|
||||
}
|
||||
pnp_disable_dev(pnp_d);
|
||||
err = pnp_activate_dev(pnp_d);
|
||||
if (err < 0) {
|
||||
pr_warn("%s: pnp_activate_dev ret=%d\n",
|
||||
__func__, err);
|
||||
return 0;
|
||||
}
|
||||
cs->hw.isurf.reset = pnp_port_start(pnp_d, 0);
|
||||
cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1);
|
||||
cs->irq = pnp_irq(pnp_d, 0);
|
||||
|
@ -511,7 +511,6 @@ config USB_SWITCH_FSA9480
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
source "drivers/misc/iwmc3200top/Kconfig"
|
||||
source "drivers/misc/ti-st/Kconfig"
|
||||
source "drivers/misc/lis3lv02d/Kconfig"
|
||||
source "drivers/misc/carma/Kconfig"
|
||||
|
@ -36,7 +36,6 @@ obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
|
||||
obj-$(CONFIG_DS1682) += ds1682.o
|
||||
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
|
||||
obj-$(CONFIG_C2PORT) += c2port/
|
||||
obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
|
||||
obj-$(CONFIG_HMC6352) += hmc6352.o
|
||||
obj-y += eeprom/
|
||||
obj-y += cb710/
|
||||
|
@ -1,20 +0,0 @@
|
||||
config IWMC3200TOP
|
||||
tristate "Intel Wireless MultiCom Top Driver"
|
||||
depends on MMC && EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
---help---
|
||||
Intel Wireless MultiCom 3200 Top driver is responsible for
|
||||
for firmware load and enabled coms enumeration
|
||||
|
||||
config IWMC3200TOP_DEBUG
|
||||
bool "Enable full debug output of iwmc3200top Driver"
|
||||
depends on IWMC3200TOP
|
||||
---help---
|
||||
Enable full debug output of iwmc3200top Driver
|
||||
|
||||
config IWMC3200TOP_DEBUGFS
|
||||
bool "Enable Debugfs debugging interface for iwmc3200top"
|
||||
depends on IWMC3200TOP
|
||||
---help---
|
||||
Enable creation of debugfs files for iwmc3200top
|
||||
|
@ -1,29 +0,0 @@
|
||||
# iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
# drivers/misc/iwmc3200top/Makefile
|
||||
#
|
||||
# Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
# Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
# -
|
||||
#
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o
|
||||
iwmc3200top-objs := main.o fw-download.o
|
||||
iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o
|
||||
iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o
|
@ -1,137 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/debufs.c
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "iwmc3200top.h"
|
||||
#include "fw-msg.h"
|
||||
#include "log.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
|
||||
|
||||
/* Constants definition */
|
||||
#define HEXADECIMAL_RADIX 16
|
||||
|
||||
/* Functions definition */
|
||||
|
||||
|
||||
#define DEBUGFS_ADD(name, parent) do { \
|
||||
dbgfs->dbgfs_##parent##_files.file_##name = \
|
||||
debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \
|
||||
&iwmct_dbgfs_##name##_ops); \
|
||||
} while (0)
|
||||
|
||||
#define DEBUGFS_RM(name) do { \
|
||||
debugfs_remove(name); \
|
||||
name = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define DEBUGFS_READ_FUNC(name) \
|
||||
ssize_t iwmct_dbgfs_##name##_read(struct file *file, \
|
||||
char __user *user_buf, \
|
||||
size_t count, loff_t *ppos);
|
||||
|
||||
#define DEBUGFS_WRITE_FUNC(name) \
|
||||
ssize_t iwmct_dbgfs_##name##_write(struct file *file, \
|
||||
const char __user *user_buf, \
|
||||
size_t count, loff_t *ppos);
|
||||
|
||||
#define DEBUGFS_READ_FILE_OPS(name) \
|
||||
DEBUGFS_READ_FUNC(name) \
|
||||
static const struct file_operations iwmct_dbgfs_##name##_ops = { \
|
||||
.read = iwmct_dbgfs_##name##_read, \
|
||||
.open = iwmct_dbgfs_open_file_generic, \
|
||||
.llseek = generic_file_llseek, \
|
||||
};
|
||||
|
||||
#define DEBUGFS_WRITE_FILE_OPS(name) \
|
||||
DEBUGFS_WRITE_FUNC(name) \
|
||||
static const struct file_operations iwmct_dbgfs_##name##_ops = { \
|
||||
.write = iwmct_dbgfs_##name##_write, \
|
||||
.open = iwmct_dbgfs_open_file_generic, \
|
||||
.llseek = generic_file_llseek, \
|
||||
};
|
||||
|
||||
#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
|
||||
DEBUGFS_READ_FUNC(name) \
|
||||
DEBUGFS_WRITE_FUNC(name) \
|
||||
static const struct file_operations iwmct_dbgfs_##name##_ops = {\
|
||||
.write = iwmct_dbgfs_##name##_write, \
|
||||
.read = iwmct_dbgfs_##name##_read, \
|
||||
.open = iwmct_dbgfs_open_file_generic, \
|
||||
.llseek = generic_file_llseek, \
|
||||
};
|
||||
|
||||
|
||||
/* Debugfs file ops definitions */
|
||||
|
||||
/*
|
||||
* Create the debugfs files and directories
|
||||
*
|
||||
*/
|
||||
void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
|
||||
{
|
||||
struct iwmct_debugfs *dbgfs;
|
||||
|
||||
dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL);
|
||||
if (!dbgfs) {
|
||||
LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n",
|
||||
sizeof(struct iwmct_debugfs));
|
||||
return;
|
||||
}
|
||||
|
||||
priv->dbgfs = dbgfs;
|
||||
dbgfs->name = name;
|
||||
dbgfs->dir_drv = debugfs_create_dir(name, NULL);
|
||||
if (!dbgfs->dir_drv) {
|
||||
LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the debugfs files and directories
|
||||
*
|
||||
*/
|
||||
void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
|
||||
{
|
||||
if (!dbgfs)
|
||||
return;
|
||||
|
||||
DEBUGFS_RM(dbgfs->dir_drv);
|
||||
kfree(dbgfs);
|
||||
dbgfs = NULL;
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/debufs.h
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DEBUGFS_H__
|
||||
#define __DEBUGFS_H__
|
||||
|
||||
|
||||
#ifdef CONFIG_IWMC3200TOP_DEBUGFS
|
||||
|
||||
struct iwmct_debugfs {
|
||||
const char *name;
|
||||
struct dentry *dir_drv;
|
||||
struct dir_drv_files {
|
||||
} dbgfs_drv_files;
|
||||
};
|
||||
|
||||
void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name);
|
||||
void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs);
|
||||
|
||||
#else /* CONFIG_IWMC3200TOP_DEBUGFS */
|
||||
|
||||
struct iwmct_debugfs;
|
||||
|
||||
static inline void
|
||||
iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
|
||||
{}
|
||||
|
||||
static inline void
|
||||
iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_IWMC3200TOP_DEBUGFS */
|
||||
|
||||
#endif /* __DEBUGFS_H__ */
|
||||
|
@ -1,358 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/fw-download.c
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "iwmc3200top.h"
|
||||
#include "log.h"
|
||||
#include "fw-msg.h"
|
||||
|
||||
#define CHECKSUM_BYTES_NUM sizeof(u32)
|
||||
|
||||
/**
|
||||
init parser struct with file
|
||||
*/
|
||||
static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file,
|
||||
size_t file_size, size_t block_size)
|
||||
{
|
||||
struct iwmct_parser *parser = &priv->parser;
|
||||
struct iwmct_fw_hdr *fw_hdr = &parser->versions;
|
||||
|
||||
LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
|
||||
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size);
|
||||
|
||||
parser->file = file;
|
||||
parser->file_size = file_size;
|
||||
parser->cur_pos = 0;
|
||||
parser->entry_point = 0;
|
||||
parser->buf = kzalloc(block_size, GFP_KERNEL);
|
||||
if (!parser->buf) {
|
||||
LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
parser->buf_size = block_size;
|
||||
|
||||
/* extract fw versions */
|
||||
memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr));
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n"
|
||||
"top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n",
|
||||
fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision,
|
||||
fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision,
|
||||
fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision,
|
||||
fw_hdr->tic_name);
|
||||
|
||||
parser->cur_pos += sizeof(struct iwmct_fw_hdr);
|
||||
|
||||
LOG_TRACE(priv, FW_DOWNLOAD, "<--\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool iwmct_checksum(struct iwmct_priv *priv)
|
||||
{
|
||||
struct iwmct_parser *parser = &priv->parser;
|
||||
__le32 *file = (__le32 *)parser->file;
|
||||
int i, pad, steps;
|
||||
u32 accum = 0;
|
||||
u32 checksum;
|
||||
u32 mask = 0xffffffff;
|
||||
|
||||
pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4;
|
||||
steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4;
|
||||
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps);
|
||||
|
||||
for (i = 0; i < steps; i++)
|
||||
accum += le32_to_cpu(file[i]);
|
||||
|
||||
if (pad) {
|
||||
mask <<= 8 * (4 - pad);
|
||||
accum += le32_to_cpu(file[steps]) & mask;
|
||||
}
|
||||
|
||||
checksum = get_unaligned_le32((__le32 *)(parser->file +
|
||||
parser->file_size - CHECKSUM_BYTES_NUM));
|
||||
|
||||
LOG_INFO(priv, FW_DOWNLOAD,
|
||||
"compare checksum accum=0x%x to checksum=0x%x\n",
|
||||
accum, checksum);
|
||||
|
||||
return checksum == accum;
|
||||
}
|
||||
|
||||
static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec,
|
||||
size_t *sec_size, __le32 *sec_addr)
|
||||
{
|
||||
struct iwmct_parser *parser = &priv->parser;
|
||||
struct iwmct_dbg *dbg = &priv->dbg;
|
||||
struct iwmct_fw_sec_hdr *sec_hdr;
|
||||
|
||||
LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
|
||||
|
||||
while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr)
|
||||
<= parser->file_size) {
|
||||
|
||||
sec_hdr = (struct iwmct_fw_sec_hdr *)
|
||||
(parser->file + parser->cur_pos);
|
||||
parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr);
|
||||
|
||||
LOG_INFO(priv, FW_DOWNLOAD,
|
||||
"sec hdr: type=%s addr=0x%x size=%d\n",
|
||||
sec_hdr->type, sec_hdr->target_addr,
|
||||
sec_hdr->data_size);
|
||||
|
||||
if (strcmp(sec_hdr->type, "ENT") == 0)
|
||||
parser->entry_point = le32_to_cpu(sec_hdr->target_addr);
|
||||
else if (strcmp(sec_hdr->type, "LBL") == 0)
|
||||
strcpy(dbg->label_fw, parser->file + parser->cur_pos);
|
||||
else if (((strcmp(sec_hdr->type, "TOP") == 0) &&
|
||||
(priv->barker & BARKER_DNLOAD_TOP_MSK)) ||
|
||||
((strcmp(sec_hdr->type, "GPS") == 0) &&
|
||||
(priv->barker & BARKER_DNLOAD_GPS_MSK)) ||
|
||||
((strcmp(sec_hdr->type, "BTH") == 0) &&
|
||||
(priv->barker & BARKER_DNLOAD_BT_MSK))) {
|
||||
*sec_addr = sec_hdr->target_addr;
|
||||
*sec_size = le32_to_cpu(sec_hdr->data_size);
|
||||
*p_sec = parser->file + parser->cur_pos;
|
||||
parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
|
||||
return 1;
|
||||
} else if (strcmp(sec_hdr->type, "LOG") != 0)
|
||||
LOG_WARNING(priv, FW_DOWNLOAD,
|
||||
"skipping section type %s\n",
|
||||
sec_hdr->type);
|
||||
|
||||
parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
|
||||
LOG_INFO(priv, FW_DOWNLOAD,
|
||||
"finished with section cur_pos=%zd\n", parser->cur_pos);
|
||||
}
|
||||
|
||||
LOG_TRACE(priv, INIT, "<--\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec,
|
||||
size_t sec_size, __le32 addr)
|
||||
{
|
||||
struct iwmct_parser *parser = &priv->parser;
|
||||
struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
|
||||
const u8 *cur_block = p_sec;
|
||||
size_t sent = 0;
|
||||
int cnt = 0;
|
||||
int ret = 0;
|
||||
u32 cmd = 0;
|
||||
|
||||
LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n",
|
||||
addr, sec_size);
|
||||
|
||||
while (sent < sec_size) {
|
||||
int i;
|
||||
u32 chksm = 0;
|
||||
u32 reset = atomic_read(&priv->reset);
|
||||
/* actual FW data */
|
||||
u32 data_size = min(parser->buf_size - sizeof(*hdr),
|
||||
sec_size - sent);
|
||||
/* Pad to block size */
|
||||
u32 trans_size = (data_size + sizeof(*hdr) +
|
||||
IWMC_SDIO_BLK_SIZE - 1) &
|
||||
~(IWMC_SDIO_BLK_SIZE - 1);
|
||||
++cnt;
|
||||
|
||||
/* in case of reset, interrupt FW DOWNLAOD */
|
||||
if (reset) {
|
||||
LOG_INFO(priv, FW_DOWNLOAD,
|
||||
"Reset detected. Abort FW download!!!");
|
||||
ret = -ECANCELED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(parser->buf, 0, parser->buf_size);
|
||||
cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS;
|
||||
cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
|
||||
cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS;
|
||||
cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS;
|
||||
hdr->data_size = cpu_to_le32(data_size);
|
||||
hdr->target_addr = addr;
|
||||
|
||||
/* checksum is allowed for sizes divisible by 4 */
|
||||
if (data_size & 0x3)
|
||||
cmd &= ~CMD_HDR_USE_CHECKSUM_MSK;
|
||||
|
||||
memcpy(hdr->data, cur_block, data_size);
|
||||
|
||||
|
||||
if (cmd & CMD_HDR_USE_CHECKSUM_MSK) {
|
||||
|
||||
chksm = data_size + le32_to_cpu(addr) + cmd;
|
||||
for (i = 0; i < data_size >> 2; i++)
|
||||
chksm += ((u32 *)cur_block)[i];
|
||||
|
||||
hdr->block_chksm = cpu_to_le32(chksm);
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n",
|
||||
hdr->block_chksm);
|
||||
}
|
||||
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, "
|
||||
"sec_size=%zd, startAddress 0x%X\n",
|
||||
cnt, trans_size, sent, sec_size, addr);
|
||||
|
||||
if (priv->dbg.dump)
|
||||
LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size);
|
||||
|
||||
|
||||
hdr->cmd = cpu_to_le32(cmd);
|
||||
/* send it down */
|
||||
/* TODO: add more proper sending and error checking */
|
||||
ret = iwmct_tx(priv, parser->buf, trans_size);
|
||||
if (ret != 0) {
|
||||
LOG_INFO(priv, FW_DOWNLOAD,
|
||||
"iwmct_tx returned %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
addr = cpu_to_le32(le32_to_cpu(addr) + data_size);
|
||||
sent += data_size;
|
||||
cur_block = p_sec + sent;
|
||||
|
||||
if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) {
|
||||
LOG_INFO(priv, FW_DOWNLOAD,
|
||||
"Block number limit is reached [%d]\n",
|
||||
priv->dbg.blocks);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sent < sec_size)
|
||||
ret = -EINVAL;
|
||||
exit:
|
||||
LOG_TRACE(priv, FW_DOWNLOAD, "<--\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump)
|
||||
{
|
||||
struct iwmct_parser *parser = &priv->parser;
|
||||
struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
|
||||
int ret;
|
||||
u32 cmd;
|
||||
|
||||
LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
|
||||
|
||||
memset(parser->buf, 0, parser->buf_size);
|
||||
cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
|
||||
if (jump) {
|
||||
cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS;
|
||||
hdr->target_addr = cpu_to_le32(parser->entry_point);
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n",
|
||||
parser->entry_point);
|
||||
} else {
|
||||
cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS;
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "last command\n");
|
||||
}
|
||||
|
||||
hdr->cmd = cpu_to_le32(cmd);
|
||||
|
||||
LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr));
|
||||
/* send it down */
|
||||
/* TODO: add more proper sending and error checking */
|
||||
ret = iwmct_tx(priv, parser->buf, IWMC_SDIO_BLK_SIZE);
|
||||
if (ret)
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret);
|
||||
|
||||
LOG_TRACE(priv, FW_DOWNLOAD, "<--\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwmct_fw_load(struct iwmct_priv *priv)
|
||||
{
|
||||
const u8 *fw_name = FW_NAME(FW_API_VER);
|
||||
const struct firmware *raw;
|
||||
const u8 *pdata;
|
||||
size_t len;
|
||||
__le32 addr;
|
||||
int ret;
|
||||
|
||||
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "barker download request 0x%x is:\n",
|
||||
priv->barker);
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "******* Top FW %s requested ********\n",
|
||||
(priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not");
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "******* GPS FW %s requested ********\n",
|
||||
(priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not");
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "******* BT FW %s requested ********\n",
|
||||
(priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not");
|
||||
|
||||
|
||||
/* get the firmware */
|
||||
ret = request_firmware(&raw, fw_name, &priv->func->dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n",
|
||||
fw_name, ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) {
|
||||
LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n",
|
||||
fw_name, sizeof(struct iwmct_fw_sec_hdr), raw->size);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", fw_name);
|
||||
|
||||
/* clear parser struct */
|
||||
ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(priv, FW_DOWNLOAD,
|
||||
"iwmct_parser_init failed: Reason %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!iwmct_checksum(priv)) {
|
||||
LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* download firmware to device */
|
||||
while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) {
|
||||
ret = iwmct_download_section(priv, pdata, len, addr);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, FW_DOWNLOAD,
|
||||
"%s download section failed\n", fw_name);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK));
|
||||
|
||||
exit:
|
||||
kfree(priv->parser.buf);
|
||||
release_firmware(raw);
|
||||
return ret;
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/fw-msg.h
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __FWMSG_H__
|
||||
#define __FWMSG_H__
|
||||
|
||||
#define COMM_TYPE_D2H 0xFF
|
||||
#define COMM_TYPE_H2D 0xEE
|
||||
|
||||
#define COMM_CATEGORY_OPERATIONAL 0x00
|
||||
#define COMM_CATEGORY_DEBUG 0x01
|
||||
#define COMM_CATEGORY_TESTABILITY 0x02
|
||||
#define COMM_CATEGORY_DIAGNOSTICS 0x03
|
||||
|
||||
#define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A)
|
||||
|
||||
#define FW_LOG_SRC_MAX 32
|
||||
#define FW_LOG_SRC_ALL 255
|
||||
|
||||
#define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000)
|
||||
|
||||
#define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001)
|
||||
#define CMD_TST_DEV_RESET cpu_to_le16(0x0060)
|
||||
#define CMD_TST_FUNC_RESET cpu_to_le16(0x0062)
|
||||
#define CMD_TST_IFACE_RESET cpu_to_le16(0x0064)
|
||||
#define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065)
|
||||
#define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080)
|
||||
#define CMD_TST_WAKEUP cpu_to_le16(0x0081)
|
||||
#define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082)
|
||||
#define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083)
|
||||
#define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096)
|
||||
|
||||
#define OP_OPR_ALIVE cpu_to_le16(0x0010)
|
||||
#define OP_OPR_CMD_ACK cpu_to_le16(0x001F)
|
||||
#define OP_OPR_CMD_NACK cpu_to_le16(0x0020)
|
||||
#define OP_TST_MEM_DUMP cpu_to_le16(0x0043)
|
||||
|
||||
#define CMD_FLAG_PADDING_256 0x80
|
||||
|
||||
#define FW_HCMD_BLOCK_SIZE 256
|
||||
|
||||
struct msg_hdr {
|
||||
u8 type;
|
||||
u8 category;
|
||||
__le16 opcode;
|
||||
u8 seqnum;
|
||||
u8 flags;
|
||||
__le16 length;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct log_hdr {
|
||||
__le32 timestamp;
|
||||
u8 severity;
|
||||
u8 logsource;
|
||||
__le16 reserved;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct mdump_hdr {
|
||||
u8 dmpid;
|
||||
u8 frag;
|
||||
__le16 size;
|
||||
__le32 addr;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct top_msg {
|
||||
struct msg_hdr hdr;
|
||||
union {
|
||||
/* D2H messages */
|
||||
struct {
|
||||
struct log_hdr log_hdr;
|
||||
u8 data[1];
|
||||
} __attribute__((__packed__)) log;
|
||||
|
||||
struct {
|
||||
struct log_hdr log_hdr;
|
||||
struct mdump_hdr md_hdr;
|
||||
u8 data[1];
|
||||
} __attribute__((__packed__)) mdump;
|
||||
|
||||
/* H2D messages */
|
||||
struct {
|
||||
u8 logsource;
|
||||
u8 sevmask;
|
||||
} __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX];
|
||||
struct mdump_hdr mdump_req;
|
||||
} u;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
#endif /* __FWMSG_H__ */
|
@ -1,205 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/iwmc3200top.h
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWMC3200TOP_H__
|
||||
#define __IWMC3200TOP_H__
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define DRV_NAME "iwmc3200top"
|
||||
#define FW_API_VER 1
|
||||
#define _FW_NAME(api) DRV_NAME "." #api ".fw"
|
||||
#define FW_NAME(api) _FW_NAME(api)
|
||||
|
||||
#define IWMC_SDIO_BLK_SIZE 256
|
||||
#define IWMC_DEFAULT_TR_BLK 64
|
||||
#define IWMC_SDIO_DATA_ADDR 0x0
|
||||
#define IWMC_SDIO_INTR_ENABLE_ADDR 0x14
|
||||
#define IWMC_SDIO_INTR_STATUS_ADDR 0x13
|
||||
#define IWMC_SDIO_INTR_CLEAR_ADDR 0x13
|
||||
#define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C
|
||||
|
||||
#define COMM_HUB_HEADER_LENGTH 16
|
||||
#define LOGGER_HEADER_LENGTH 10
|
||||
|
||||
|
||||
#define BARKER_DNLOAD_BT_POS 0
|
||||
#define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS)
|
||||
#define BARKER_DNLOAD_GPS_POS 1
|
||||
#define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS)
|
||||
#define BARKER_DNLOAD_TOP_POS 2
|
||||
#define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS)
|
||||
#define BARKER_DNLOAD_RESERVED1_POS 3
|
||||
#define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS)
|
||||
#define BARKER_DNLOAD_JUMP_POS 4
|
||||
#define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS)
|
||||
#define BARKER_DNLOAD_SYNC_POS 5
|
||||
#define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS)
|
||||
#define BARKER_DNLOAD_RESERVED2_POS 6
|
||||
#define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS)
|
||||
#define BARKER_DNLOAD_BARKER_POS 8
|
||||
#define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS)
|
||||
|
||||
#define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS)
|
||||
/* whole field barker */
|
||||
#define IWMC_BARKER_ACK 0xfeedbabe
|
||||
|
||||
#define IWMC_CMD_SIGNATURE 0xcbbc
|
||||
|
||||
#define CMD_HDR_OPCODE_POS 0
|
||||
#define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS)
|
||||
#define CMD_HDR_RESPONSE_CODE_POS 4
|
||||
#define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS)
|
||||
#define CMD_HDR_USE_CHECKSUM_POS 8
|
||||
#define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS)
|
||||
#define CMD_HDR_RESPONSE_REQUIRED_POS 9
|
||||
#define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS)
|
||||
#define CMD_HDR_DIRECT_ACCESS_POS 10
|
||||
#define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS)
|
||||
#define CMD_HDR_RESERVED_POS 11
|
||||
#define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS)
|
||||
#define CMD_HDR_SIGNATURE_POS 16
|
||||
#define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS)
|
||||
|
||||
enum {
|
||||
IWMC_OPCODE_PING = 0,
|
||||
IWMC_OPCODE_READ = 1,
|
||||
IWMC_OPCODE_WRITE = 2,
|
||||
IWMC_OPCODE_JUMP = 3,
|
||||
IWMC_OPCODE_REBOOT = 4,
|
||||
IWMC_OPCODE_PERSISTENT_WRITE = 5,
|
||||
IWMC_OPCODE_PERSISTENT_READ = 6,
|
||||
IWMC_OPCODE_READ_MODIFY_WRITE = 7,
|
||||
IWMC_OPCODE_LAST_COMMAND = 15
|
||||
};
|
||||
|
||||
struct iwmct_fw_load_hdr {
|
||||
__le32 cmd;
|
||||
__le32 target_addr;
|
||||
__le32 data_size;
|
||||
__le32 block_chksm;
|
||||
u8 data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwmct_fw_hdr
|
||||
* holds all sw components versions
|
||||
*/
|
||||
struct iwmct_fw_hdr {
|
||||
u8 top_major;
|
||||
u8 top_minor;
|
||||
u8 top_revision;
|
||||
u8 gps_major;
|
||||
u8 gps_minor;
|
||||
u8 gps_revision;
|
||||
u8 bt_major;
|
||||
u8 bt_minor;
|
||||
u8 bt_revision;
|
||||
u8 tic_name[31];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwmct_fw_sec_hdr
|
||||
* @type: function type
|
||||
* @data_size: section's data size
|
||||
* @target_addr: download address
|
||||
*/
|
||||
struct iwmct_fw_sec_hdr {
|
||||
u8 type[4];
|
||||
__le32 data_size;
|
||||
__le32 target_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwmct_parser
|
||||
* @file: fw image
|
||||
* @file_size: fw size
|
||||
* @cur_pos: position in file
|
||||
* @buf: temp buf for download
|
||||
* @buf_size: size of buf
|
||||
* @entry_point: address to jump in fw kick-off
|
||||
*/
|
||||
struct iwmct_parser {
|
||||
const u8 *file;
|
||||
size_t file_size;
|
||||
size_t cur_pos;
|
||||
u8 *buf;
|
||||
size_t buf_size;
|
||||
u32 entry_point;
|
||||
struct iwmct_fw_hdr versions;
|
||||
};
|
||||
|
||||
|
||||
struct iwmct_work_struct {
|
||||
struct list_head list;
|
||||
ssize_t iosize;
|
||||
};
|
||||
|
||||
struct iwmct_dbg {
|
||||
int blocks;
|
||||
bool dump;
|
||||
bool jump;
|
||||
bool direct;
|
||||
bool checksum;
|
||||
bool fw_download;
|
||||
int block_size;
|
||||
int download_trans_blks;
|
||||
|
||||
char label_fw[256];
|
||||
};
|
||||
|
||||
struct iwmct_debugfs;
|
||||
|
||||
struct iwmct_priv {
|
||||
struct sdio_func *func;
|
||||
struct iwmct_debugfs *dbgfs;
|
||||
struct iwmct_parser parser;
|
||||
atomic_t reset;
|
||||
atomic_t dev_sync;
|
||||
u32 trans_len;
|
||||
u32 barker;
|
||||
struct iwmct_dbg dbg;
|
||||
|
||||
/* drivers work items */
|
||||
struct work_struct bus_rescan_worker;
|
||||
struct work_struct isr_worker;
|
||||
|
||||
/* drivers wait queue */
|
||||
wait_queue_head_t wait_q;
|
||||
|
||||
/* rx request list */
|
||||
struct list_head read_req_list;
|
||||
};
|
||||
|
||||
extern int iwmct_tx(struct iwmct_priv *priv, void *src, int count);
|
||||
extern int iwmct_fw_load(struct iwmct_priv *priv);
|
||||
|
||||
extern void iwmct_dbg_init_params(struct iwmct_priv *drv);
|
||||
extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv);
|
||||
extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv);
|
||||
extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len);
|
||||
|
||||
#endif /* __IWMC3200TOP_H__ */
|
@ -1,348 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/log.c
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#include "fw-msg.h"
|
||||
#include "iwmc3200top.h"
|
||||
#include "log.h"
|
||||
|
||||
/* Maximal hexadecimal string size of the FW memdump message */
|
||||
#define LOG_MSG_SIZE_MAX 12400
|
||||
|
||||
/* iwmct_logdefs is a global used by log macros */
|
||||
u8 iwmct_logdefs[LOG_SRC_MAX];
|
||||
static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
|
||||
|
||||
|
||||
static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (src < size)
|
||||
logdefs[src] = logmask;
|
||||
else if (src == LOG_SRC_ALL)
|
||||
for (i = 0; i < size; i++)
|
||||
logdefs[i] = logmask;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int iwmct_log_set_filter(u8 src, u8 logmask)
|
||||
{
|
||||
return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
|
||||
}
|
||||
|
||||
|
||||
int iwmct_log_set_fw_filter(u8 src, u8 logmask)
|
||||
{
|
||||
return _log_set_log_filter(iwmct_fw_logdefs,
|
||||
FW_LOG_SRC_MAX, src, logmask);
|
||||
}
|
||||
|
||||
|
||||
static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
|
||||
int ilen, char *pref)
|
||||
{
|
||||
int pos = 0;
|
||||
int i;
|
||||
int len;
|
||||
|
||||
for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
|
||||
str[pos] = pref[i];
|
||||
|
||||
for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
|
||||
len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
|
||||
|
||||
if (i < ilen)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: This function is not thread safe.
|
||||
Currently it's called only from sdio rx worker - no race there
|
||||
*/
|
||||
void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
|
||||
{
|
||||
struct top_msg *msg;
|
||||
static char logbuf[LOG_MSG_SIZE_MAX];
|
||||
|
||||
msg = (struct top_msg *)buf;
|
||||
|
||||
if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
|
||||
LOG_ERROR(priv, FW_MSG, "Log message from TOP "
|
||||
"is too short %d (expected %zd)\n",
|
||||
len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
|
||||
BIT(msg->u.log.log_hdr.severity)) ||
|
||||
!(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
|
||||
return;
|
||||
|
||||
switch (msg->hdr.category) {
|
||||
case COMM_CATEGORY_TESTABILITY:
|
||||
if (!(iwmct_logdefs[LOG_SRC_TST] &
|
||||
BIT(msg->u.log.log_hdr.severity)))
|
||||
return;
|
||||
if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
|
||||
le16_to_cpu(msg->hdr.length) +
|
||||
sizeof(msg->hdr), "<TST>"))
|
||||
LOG_WARNING(priv, TST,
|
||||
"TOP TST message is too long, truncating...");
|
||||
LOG_WARNING(priv, TST, "%s\n", logbuf);
|
||||
break;
|
||||
case COMM_CATEGORY_DEBUG:
|
||||
if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
|
||||
LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
|
||||
((u8 *)msg) + sizeof(msg->hdr)
|
||||
+ sizeof(msg->u.log.log_hdr));
|
||||
else {
|
||||
if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
|
||||
le16_to_cpu(msg->hdr.length)
|
||||
+ sizeof(msg->hdr),
|
||||
"<DBG>"))
|
||||
LOG_WARNING(priv, FW_MSG,
|
||||
"TOP DBG message is too long,"
|
||||
"truncating...");
|
||||
LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
|
||||
{
|
||||
int i, pos, len;
|
||||
for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
|
||||
len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
|
||||
i, logdefs[i]);
|
||||
pos += len;
|
||||
}
|
||||
buf[pos-1] = '\n';
|
||||
buf[pos] = '\0';
|
||||
|
||||
if (i < logdefsz)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_get_filter_str(char *buf, int size)
|
||||
{
|
||||
return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
|
||||
}
|
||||
|
||||
int log_get_fw_filter_str(char *buf, int size)
|
||||
{
|
||||
return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
|
||||
}
|
||||
|
||||
#define HEXADECIMAL_RADIX 16
|
||||
#define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */
|
||||
|
||||
ssize_t show_iwmct_log_level(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iwmct_priv *priv = dev_get_drvdata(d);
|
||||
char *str_buf;
|
||||
int buf_size;
|
||||
ssize_t ret;
|
||||
|
||||
buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
|
||||
str_buf = kzalloc(buf_size, GFP_KERNEL);
|
||||
if (!str_buf) {
|
||||
LOG_ERROR(priv, DEBUGFS,
|
||||
"failed to allocate %d bytes\n", buf_size);
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (log_get_filter_str(str_buf, buf_size) < 0) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = sprintf(buf, "%s", str_buf);
|
||||
|
||||
exit:
|
||||
kfree(str_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t store_iwmct_log_level(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct iwmct_priv *priv = dev_get_drvdata(d);
|
||||
char *token, *str_buf = NULL;
|
||||
long val;
|
||||
ssize_t ret = count;
|
||||
u8 src, mask;
|
||||
|
||||
if (!count)
|
||||
goto exit;
|
||||
|
||||
str_buf = kzalloc(count, GFP_KERNEL);
|
||||
if (!str_buf) {
|
||||
LOG_ERROR(priv, DEBUGFS,
|
||||
"failed to allocate %zd bytes\n", count);
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy(str_buf, buf, count);
|
||||
|
||||
while ((token = strsep(&str_buf, ",")) != NULL) {
|
||||
while (isspace(*token))
|
||||
++token;
|
||||
if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
|
||||
LOG_ERROR(priv, DEBUGFS,
|
||||
"failed to convert string to long %s\n",
|
||||
token);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mask = val & 0xFF;
|
||||
src = (val & 0XFF00) >> 8;
|
||||
iwmct_log_set_filter(src, mask);
|
||||
}
|
||||
|
||||
exit:
|
||||
kfree(str_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t show_iwmct_log_level_fw(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iwmct_priv *priv = dev_get_drvdata(d);
|
||||
char *str_buf;
|
||||
int buf_size;
|
||||
ssize_t ret;
|
||||
|
||||
buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
|
||||
|
||||
str_buf = kzalloc(buf_size, GFP_KERNEL);
|
||||
if (!str_buf) {
|
||||
LOG_ERROR(priv, DEBUGFS,
|
||||
"failed to allocate %d bytes\n", buf_size);
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = sprintf(buf, "%s", str_buf);
|
||||
|
||||
exit:
|
||||
kfree(str_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t store_iwmct_log_level_fw(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct iwmct_priv *priv = dev_get_drvdata(d);
|
||||
struct top_msg cmd;
|
||||
char *token, *str_buf = NULL;
|
||||
ssize_t ret = count;
|
||||
u16 cmdlen = 0;
|
||||
int i;
|
||||
long val;
|
||||
u8 src, mask;
|
||||
|
||||
if (!count)
|
||||
goto exit;
|
||||
|
||||
str_buf = kzalloc(count, GFP_KERNEL);
|
||||
if (!str_buf) {
|
||||
LOG_ERROR(priv, DEBUGFS,
|
||||
"failed to allocate %zd bytes\n", count);
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy(str_buf, buf, count);
|
||||
|
||||
cmd.hdr.type = COMM_TYPE_H2D;
|
||||
cmd.hdr.category = COMM_CATEGORY_DEBUG;
|
||||
cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
|
||||
|
||||
for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
|
||||
(i < FW_LOG_SRC_MAX); i++) {
|
||||
|
||||
while (isspace(*token))
|
||||
++token;
|
||||
|
||||
if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
|
||||
LOG_ERROR(priv, DEBUGFS,
|
||||
"failed to convert string to long %s\n",
|
||||
token);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mask = val & 0xFF; /* LSB */
|
||||
src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
|
||||
iwmct_log_set_fw_filter(src, mask);
|
||||
|
||||
cmd.u.logdefs[i].logsource = src;
|
||||
cmd.u.logdefs[i].sevmask = mask;
|
||||
}
|
||||
|
||||
cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
|
||||
cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
|
||||
|
||||
ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, DEBUGFS,
|
||||
"Failed to send %d bytes of fwcmd, ret=%zd\n",
|
||||
cmdlen, ret);
|
||||
goto exit;
|
||||
} else
|
||||
LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
|
||||
|
||||
ret = count;
|
||||
|
||||
exit:
|
||||
kfree(str_buf);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1,171 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/log.h
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LOG_H__
|
||||
#define __LOG_H__
|
||||
|
||||
|
||||
/* log severity:
|
||||
* The log levels here match FW log levels
|
||||
* so values need to stay as is */
|
||||
#define LOG_SEV_CRITICAL 0
|
||||
#define LOG_SEV_ERROR 1
|
||||
#define LOG_SEV_WARNING 2
|
||||
#define LOG_SEV_INFO 3
|
||||
#define LOG_SEV_INFOEX 4
|
||||
|
||||
/* Log levels not defined for FW */
|
||||
#define LOG_SEV_TRACE 5
|
||||
#define LOG_SEV_DUMP 6
|
||||
|
||||
#define LOG_SEV_FW_FILTER_ALL \
|
||||
(BIT(LOG_SEV_CRITICAL) | \
|
||||
BIT(LOG_SEV_ERROR) | \
|
||||
BIT(LOG_SEV_WARNING) | \
|
||||
BIT(LOG_SEV_INFO) | \
|
||||
BIT(LOG_SEV_INFOEX))
|
||||
|
||||
#define LOG_SEV_FILTER_ALL \
|
||||
(BIT(LOG_SEV_CRITICAL) | \
|
||||
BIT(LOG_SEV_ERROR) | \
|
||||
BIT(LOG_SEV_WARNING) | \
|
||||
BIT(LOG_SEV_INFO) | \
|
||||
BIT(LOG_SEV_INFOEX) | \
|
||||
BIT(LOG_SEV_TRACE) | \
|
||||
BIT(LOG_SEV_DUMP))
|
||||
|
||||
/* log source */
|
||||
#define LOG_SRC_INIT 0
|
||||
#define LOG_SRC_DEBUGFS 1
|
||||
#define LOG_SRC_FW_DOWNLOAD 2
|
||||
#define LOG_SRC_FW_MSG 3
|
||||
#define LOG_SRC_TST 4
|
||||
#define LOG_SRC_IRQ 5
|
||||
|
||||
#define LOG_SRC_MAX 6
|
||||
#define LOG_SRC_ALL 0xFF
|
||||
|
||||
/**
|
||||
* Default intitialization runtime log level
|
||||
*/
|
||||
#ifndef LOG_SEV_FILTER_RUNTIME
|
||||
#define LOG_SEV_FILTER_RUNTIME \
|
||||
(BIT(LOG_SEV_CRITICAL) | \
|
||||
BIT(LOG_SEV_ERROR) | \
|
||||
BIT(LOG_SEV_WARNING))
|
||||
#endif
|
||||
|
||||
#ifndef FW_LOG_SEV_FILTER_RUNTIME
|
||||
#define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IWMC3200TOP_DEBUG
|
||||
/**
|
||||
* Log macros
|
||||
*/
|
||||
|
||||
#define priv2dev(priv) (&(priv->func)->dev)
|
||||
|
||||
#define LOG_CRITICAL(priv, src, fmt, args...) \
|
||||
do { \
|
||||
if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \
|
||||
dev_crit(priv2dev(priv), "%s %d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_ERROR(priv, src, fmt, args...) \
|
||||
do { \
|
||||
if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \
|
||||
dev_err(priv2dev(priv), "%s %d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_WARNING(priv, src, fmt, args...) \
|
||||
do { \
|
||||
if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \
|
||||
dev_warn(priv2dev(priv), "%s %d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_INFO(priv, src, fmt, args...) \
|
||||
do { \
|
||||
if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \
|
||||
dev_info(priv2dev(priv), "%s %d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_TRACE(priv, src, fmt, args...) \
|
||||
do { \
|
||||
if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_TRACE)) \
|
||||
dev_dbg(priv2dev(priv), "%s %d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_HEXDUMP(src, ptr, len) \
|
||||
do { \
|
||||
if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_DUMP)) \
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \
|
||||
16, 1, ptr, len, false); \
|
||||
} while (0)
|
||||
|
||||
void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len);
|
||||
|
||||
extern u8 iwmct_logdefs[];
|
||||
|
||||
int iwmct_log_set_filter(u8 src, u8 logmask);
|
||||
int iwmct_log_set_fw_filter(u8 src, u8 logmask);
|
||||
|
||||
ssize_t show_iwmct_log_level(struct device *d,
|
||||
struct device_attribute *attr, char *buf);
|
||||
ssize_t store_iwmct_log_level(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
ssize_t show_iwmct_log_level_fw(struct device *d,
|
||||
struct device_attribute *attr, char *buf);
|
||||
ssize_t store_iwmct_log_level_fw(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
#else
|
||||
|
||||
#define LOG_CRITICAL(priv, src, fmt, args...)
|
||||
#define LOG_ERROR(priv, src, fmt, args...)
|
||||
#define LOG_WARNING(priv, src, fmt, args...)
|
||||
#define LOG_INFO(priv, src, fmt, args...)
|
||||
#define LOG_TRACE(priv, src, fmt, args...)
|
||||
#define LOG_HEXDUMP(src, ptr, len)
|
||||
|
||||
static inline void iwmct_log_top_message(struct iwmct_priv *priv,
|
||||
u8 *buf, int len) {}
|
||||
static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; }
|
||||
static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; }
|
||||
|
||||
#endif /* CONFIG_IWMC3200TOP_DEBUG */
|
||||
|
||||
int log_get_filter_str(char *buf, int size);
|
||||
int log_get_fw_filter_str(char *buf, int size);
|
||||
|
||||
#endif /* __LOG_H__ */
|
@ -1,662 +0,0 @@
|
||||
/*
|
||||
* iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
|
||||
* drivers/misc/iwmc3200top/main.c
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
|
||||
* -
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
|
||||
#include "iwmc3200top.h"
|
||||
#include "log.h"
|
||||
#include "fw-msg.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
|
||||
#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver"
|
||||
#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation."
|
||||
|
||||
#define DRIVER_VERSION "0.1.62"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR(DRIVER_COPYRIGHT);
|
||||
MODULE_FIRMWARE(FW_NAME(FW_API_VER));
|
||||
|
||||
|
||||
static inline int __iwmct_tx(struct iwmct_priv *priv, void *src, int count)
|
||||
{
|
||||
return sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, src, count);
|
||||
|
||||
}
|
||||
int iwmct_tx(struct iwmct_priv *priv, void *src, int count)
|
||||
{
|
||||
int ret;
|
||||
sdio_claim_host(priv->func);
|
||||
ret = __iwmct_tx(priv, src, count);
|
||||
sdio_release_host(priv->func);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* This workers main task is to wait for OP_OPR_ALIVE
|
||||
* from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed.
|
||||
* When OP_OPR_ALIVE received it will issue
|
||||
* a call to "bus_rescan_devices".
|
||||
*/
|
||||
static void iwmct_rescan_worker(struct work_struct *ws)
|
||||
{
|
||||
struct iwmct_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = container_of(ws, struct iwmct_priv, bus_rescan_worker);
|
||||
|
||||
LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n");
|
||||
|
||||
ret = bus_rescan_devices(priv->func->dev.bus);
|
||||
if (ret < 0)
|
||||
LOG_INFO(priv, INIT, "bus_rescan_devices FAILED!!!\n");
|
||||
}
|
||||
|
||||
static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg)
|
||||
{
|
||||
switch (msg->hdr.opcode) {
|
||||
case OP_OPR_ALIVE:
|
||||
LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n");
|
||||
schedule_work(&priv->bus_rescan_worker);
|
||||
break;
|
||||
default:
|
||||
LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n",
|
||||
msg->hdr.opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len)
|
||||
{
|
||||
struct top_msg *msg;
|
||||
|
||||
msg = (struct top_msg *)buf;
|
||||
|
||||
if (msg->hdr.type != COMM_TYPE_D2H) {
|
||||
LOG_ERROR(priv, FW_MSG,
|
||||
"Message from TOP with invalid message type 0x%X\n",
|
||||
msg->hdr.type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < sizeof(msg->hdr)) {
|
||||
LOG_ERROR(priv, FW_MSG,
|
||||
"Message from TOP is too short for message header "
|
||||
"received %d bytes, expected at least %zd bytes\n",
|
||||
len, sizeof(msg->hdr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) {
|
||||
LOG_ERROR(priv, FW_MSG,
|
||||
"Message length (%d bytes) is shorter than "
|
||||
"in header (%d bytes)\n",
|
||||
len, le16_to_cpu(msg->hdr.length));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg->hdr.category) {
|
||||
case COMM_CATEGORY_OPERATIONAL:
|
||||
op_top_message(priv, (struct top_msg *)buf);
|
||||
break;
|
||||
|
||||
case COMM_CATEGORY_DEBUG:
|
||||
case COMM_CATEGORY_TESTABILITY:
|
||||
case COMM_CATEGORY_DIAGNOSTICS:
|
||||
iwmct_log_top_message(priv, buf, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(priv, FW_MSG,
|
||||
"Message from TOP with unknown category 0x%X\n",
|
||||
msg->hdr.category);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len)
|
||||
{
|
||||
int ret;
|
||||
u8 *buf;
|
||||
|
||||
LOG_TRACE(priv, FW_MSG, "Sending hcmd:\n");
|
||||
|
||||
/* add padding to 256 for IWMC */
|
||||
((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256;
|
||||
|
||||
LOG_HEXDUMP(FW_MSG, cmd, len);
|
||||
|
||||
if (len > FW_HCMD_BLOCK_SIZE) {
|
||||
LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n",
|
||||
len, FW_HCMD_BLOCK_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n",
|
||||
FW_HCMD_BLOCK_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf, cmd, len);
|
||||
ret = iwmct_tx(priv, buf, FW_HCMD_BLOCK_SIZE);
|
||||
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void iwmct_irq_read_worker(struct work_struct *ws)
|
||||
{
|
||||
struct iwmct_priv *priv;
|
||||
struct iwmct_work_struct *read_req;
|
||||
__le32 *buf = NULL;
|
||||
int ret;
|
||||
int iosize;
|
||||
u32 barker;
|
||||
bool is_barker;
|
||||
|
||||
priv = container_of(ws, struct iwmct_priv, isr_worker);
|
||||
|
||||
LOG_TRACE(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws);
|
||||
|
||||
/* --------------------- Handshake with device -------------------- */
|
||||
sdio_claim_host(priv->func);
|
||||
|
||||
/* all list manipulations have to be protected by
|
||||
* sdio_claim_host/sdio_release_host */
|
||||
if (list_empty(&priv->read_req_list)) {
|
||||
LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n");
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
read_req = list_entry(priv->read_req_list.next,
|
||||
struct iwmct_work_struct, list);
|
||||
|
||||
list_del(&read_req->list);
|
||||
iosize = read_req->iosize;
|
||||
kfree(read_req);
|
||||
|
||||
buf = kzalloc(iosize, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize);
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n",
|
||||
iosize, buf, priv->func->num);
|
||||
|
||||
/* read from device */
|
||||
ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret);
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
LOG_HEXDUMP(IRQ, (u8 *)buf, iosize);
|
||||
|
||||
barker = le32_to_cpu(buf[0]);
|
||||
|
||||
/* Verify whether it's a barker and if not - treat as regular Rx */
|
||||
if (barker == IWMC_BARKER_ACK ||
|
||||
(barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) {
|
||||
|
||||
/* Valid Barker is equal on first 4 dwords */
|
||||
is_barker = (buf[1] == buf[0]) &&
|
||||
(buf[2] == buf[0]) &&
|
||||
(buf[3] == buf[0]);
|
||||
|
||||
if (!is_barker) {
|
||||
LOG_WARNING(priv, IRQ,
|
||||
"Potentially inconsistent barker "
|
||||
"%08X_%08X_%08X_%08X\n",
|
||||
le32_to_cpu(buf[0]), le32_to_cpu(buf[1]),
|
||||
le32_to_cpu(buf[2]), le32_to_cpu(buf[3]));
|
||||
}
|
||||
} else {
|
||||
is_barker = false;
|
||||
}
|
||||
|
||||
/* Handle Top CommHub message */
|
||||
if (!is_barker) {
|
||||
sdio_release_host(priv->func);
|
||||
handle_top_message(priv, (u8 *)buf, iosize);
|
||||
goto exit;
|
||||
} else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */
|
||||
if (atomic_read(&priv->dev_sync) == 0) {
|
||||
LOG_ERROR(priv, IRQ,
|
||||
"ACK barker arrived out-of-sync\n");
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
/* Continuing to FW download (after Sync is completed)*/
|
||||
atomic_set(&priv->dev_sync, 0);
|
||||
LOG_INFO(priv, IRQ, "ACK barker arrived "
|
||||
"- starting FW download\n");
|
||||
} else { /* REBOOT barker */
|
||||
LOG_INFO(priv, IRQ, "Received reboot barker: %x\n", barker);
|
||||
priv->barker = barker;
|
||||
|
||||
if (barker & BARKER_DNLOAD_SYNC_MSK) {
|
||||
/* Send the same barker back */
|
||||
ret = __iwmct_tx(priv, buf, iosize);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, IRQ,
|
||||
"error %d echoing barker\n", ret);
|
||||
goto exit_release;
|
||||
}
|
||||
LOG_INFO(priv, IRQ, "Echoing barker to device\n");
|
||||
atomic_set(&priv->dev_sync, 1);
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
/* Continuing to FW download (without Sync) */
|
||||
LOG_INFO(priv, IRQ, "No sync requested "
|
||||
"- starting FW download\n");
|
||||
}
|
||||
|
||||
sdio_release_host(priv->func);
|
||||
|
||||
if (priv->dbg.fw_download)
|
||||
iwmct_fw_load(priv);
|
||||
else
|
||||
LOG_ERROR(priv, IRQ, "FW download not allowed\n");
|
||||
|
||||
goto exit;
|
||||
|
||||
exit_release:
|
||||
sdio_release_host(priv->func);
|
||||
exit:
|
||||
kfree(buf);
|
||||
LOG_TRACE(priv, IRQ, "exit iwmct_irq_read_worker\n");
|
||||
}
|
||||
|
||||
static void iwmct_irq(struct sdio_func *func)
|
||||
{
|
||||
struct iwmct_priv *priv;
|
||||
int val, ret;
|
||||
int iosize;
|
||||
int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR;
|
||||
struct iwmct_work_struct *read_req;
|
||||
|
||||
priv = sdio_get_drvdata(func);
|
||||
|
||||
LOG_TRACE(priv, IRQ, "enter iwmct_irq\n");
|
||||
|
||||
/* read the function's status register */
|
||||
val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret);
|
||||
|
||||
LOG_TRACE(priv, IRQ, "iir value = %d, ret=%d\n", val, ret);
|
||||
|
||||
if (!val) {
|
||||
LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n");
|
||||
goto exit_clear_intr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* read 2 bytes of the transaction size
|
||||
* IMPORTANT: sdio transaction size has to be read before clearing
|
||||
* sdio interrupt!!!
|
||||
*/
|
||||
val = sdio_readb(priv->func, addr++, &ret);
|
||||
iosize = val;
|
||||
val = sdio_readb(priv->func, addr++, &ret);
|
||||
iosize += val << 8;
|
||||
|
||||
LOG_INFO(priv, IRQ, "READ size %d\n", iosize);
|
||||
|
||||
if (iosize == 0) {
|
||||
LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize);
|
||||
goto exit_clear_intr;
|
||||
}
|
||||
|
||||
/* allocate a work structure to pass iosize to the worker */
|
||||
read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL);
|
||||
if (!read_req) {
|
||||
LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n");
|
||||
goto exit_clear_intr;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&read_req->list);
|
||||
read_req->iosize = iosize;
|
||||
|
||||
list_add_tail(&priv->read_req_list, &read_req->list);
|
||||
|
||||
/* clear the function's interrupt request bit (write 1 to clear) */
|
||||
sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
|
||||
|
||||
schedule_work(&priv->isr_worker);
|
||||
|
||||
LOG_TRACE(priv, IRQ, "exit iwmct_irq\n");
|
||||
|
||||
return;
|
||||
|
||||
exit_clear_intr:
|
||||
/* clear the function's interrupt request bit (write 1 to clear) */
|
||||
sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
|
||||
}
|
||||
|
||||
|
||||
static int blocks;
|
||||
module_param(blocks, int, 0604);
|
||||
MODULE_PARM_DESC(blocks, "max_blocks_to_send");
|
||||
|
||||
static bool dump;
|
||||
module_param(dump, bool, 0604);
|
||||
MODULE_PARM_DESC(dump, "dump_hex_content");
|
||||
|
||||
static bool jump = 1;
|
||||
module_param(jump, bool, 0604);
|
||||
|
||||
static bool direct = 1;
|
||||
module_param(direct, bool, 0604);
|
||||
|
||||
static bool checksum = 1;
|
||||
module_param(checksum, bool, 0604);
|
||||
|
||||
static bool fw_download = 1;
|
||||
module_param(fw_download, bool, 0604);
|
||||
|
||||
static int block_size = IWMC_SDIO_BLK_SIZE;
|
||||
module_param(block_size, int, 0404);
|
||||
|
||||
static int download_trans_blks = IWMC_DEFAULT_TR_BLK;
|
||||
module_param(download_trans_blks, int, 0604);
|
||||
|
||||
static bool rubbish_barker;
|
||||
module_param(rubbish_barker, bool, 0604);
|
||||
|
||||
#ifdef CONFIG_IWMC3200TOP_DEBUG
|
||||
static int log_level[LOG_SRC_MAX];
|
||||
static unsigned int log_level_argc;
|
||||
module_param_array(log_level, int, &log_level_argc, 0604);
|
||||
MODULE_PARM_DESC(log_level, "log_level");
|
||||
|
||||
static int log_level_fw[FW_LOG_SRC_MAX];
|
||||
static unsigned int log_level_fw_argc;
|
||||
module_param_array(log_level_fw, int, &log_level_fw_argc, 0604);
|
||||
MODULE_PARM_DESC(log_level_fw, "log_level_fw");
|
||||
#endif
|
||||
|
||||
void iwmct_dbg_init_params(struct iwmct_priv *priv)
|
||||
{
|
||||
#ifdef CONFIG_IWMC3200TOP_DEBUG
|
||||
int i;
|
||||
|
||||
for (i = 0; i < log_level_argc; i++) {
|
||||
dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n",
|
||||
i, log_level[i]);
|
||||
iwmct_log_set_filter((log_level[i] >> 8) & 0xFF,
|
||||
log_level[i] & 0xFF);
|
||||
}
|
||||
for (i = 0; i < log_level_fw_argc; i++) {
|
||||
dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n",
|
||||
i, log_level_fw[i]);
|
||||
iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF,
|
||||
log_level_fw[i] & 0xFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
priv->dbg.blocks = blocks;
|
||||
LOG_INFO(priv, INIT, "blocks=%d\n", blocks);
|
||||
priv->dbg.dump = (bool)dump;
|
||||
LOG_INFO(priv, INIT, "dump=%d\n", dump);
|
||||
priv->dbg.jump = (bool)jump;
|
||||
LOG_INFO(priv, INIT, "jump=%d\n", jump);
|
||||
priv->dbg.direct = (bool)direct;
|
||||
LOG_INFO(priv, INIT, "direct=%d\n", direct);
|
||||
priv->dbg.checksum = (bool)checksum;
|
||||
LOG_INFO(priv, INIT, "checksum=%d\n", checksum);
|
||||
priv->dbg.fw_download = (bool)fw_download;
|
||||
LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download);
|
||||
priv->dbg.block_size = block_size;
|
||||
LOG_INFO(priv, INIT, "block_size=%d\n", block_size);
|
||||
priv->dbg.download_trans_blks = download_trans_blks;
|
||||
LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* sysfs attributes
|
||||
*
|
||||
*****************************************************************************/
|
||||
static ssize_t show_iwmct_fw_version(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iwmct_priv *priv = dev_get_drvdata(d);
|
||||
return sprintf(buf, "%s\n", priv->dbg.label_fw);
|
||||
}
|
||||
static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL);
|
||||
|
||||
#ifdef CONFIG_IWMC3200TOP_DEBUG
|
||||
static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO,
|
||||
show_iwmct_log_level, store_iwmct_log_level);
|
||||
static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO,
|
||||
show_iwmct_log_level_fw, store_iwmct_log_level_fw);
|
||||
#endif
|
||||
|
||||
static struct attribute *iwmct_sysfs_entries[] = {
|
||||
&dev_attr_cc_label_fw.attr,
|
||||
#ifdef CONFIG_IWMC3200TOP_DEBUG
|
||||
&dev_attr_log_level.attr,
|
||||
&dev_attr_log_level_fw.attr,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group iwmct_attribute_group = {
|
||||
.name = NULL, /* put in device directory */
|
||||
.attrs = iwmct_sysfs_entries,
|
||||
};
|
||||
|
||||
|
||||
static int iwmct_probe(struct sdio_func *func,
|
||||
const struct sdio_device_id *id)
|
||||
{
|
||||
struct iwmct_priv *priv;
|
||||
int ret;
|
||||
int val = 1;
|
||||
int addr = IWMC_SDIO_INTR_ENABLE_ADDR;
|
||||
|
||||
dev_dbg(&func->dev, "enter iwmct_probe\n");
|
||||
|
||||
dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n",
|
||||
jiffies_to_msecs(2147483647), HZ);
|
||||
|
||||
priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&func->dev, "kzalloc error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
priv->func = func;
|
||||
sdio_set_drvdata(func, priv);
|
||||
|
||||
INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker);
|
||||
INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker);
|
||||
|
||||
init_waitqueue_head(&priv->wait_q);
|
||||
|
||||
sdio_claim_host(func);
|
||||
/* FIXME: Remove after it is fixed in the Boot ROM upgrade */
|
||||
func->enable_timeout = 10;
|
||||
|
||||
/* In our HW, setting the block size also wakes up the boot rom. */
|
||||
ret = sdio_set_block_size(func, priv->dbg.block_size);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, INIT,
|
||||
"sdio_set_block_size() failure: %d\n", ret);
|
||||
goto error_sdio_enable;
|
||||
}
|
||||
|
||||
ret = sdio_enable_func(func);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret);
|
||||
goto error_sdio_enable;
|
||||
}
|
||||
|
||||
/* init reset and dev_sync states */
|
||||
atomic_set(&priv->reset, 0);
|
||||
atomic_set(&priv->dev_sync, 0);
|
||||
|
||||
/* init read req queue */
|
||||
INIT_LIST_HEAD(&priv->read_req_list);
|
||||
|
||||
/* process configurable parameters */
|
||||
iwmct_dbg_init_params(priv);
|
||||
ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, INIT, "Failed to register attributes and "
|
||||
"initialize module_params\n");
|
||||
goto error_dev_attrs;
|
||||
}
|
||||
|
||||
iwmct_dbgfs_register(priv, DRV_NAME);
|
||||
|
||||
if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) {
|
||||
LOG_INFO(priv, INIT,
|
||||
"Reducing transaction to 8 blocks = 2K (from %d)\n",
|
||||
priv->dbg.download_trans_blks);
|
||||
priv->dbg.download_trans_blks = 8;
|
||||
}
|
||||
priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size;
|
||||
LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len);
|
||||
|
||||
ret = sdio_claim_irq(func, iwmct_irq);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret);
|
||||
goto error_claim_irq;
|
||||
}
|
||||
|
||||
|
||||
/* Enable function's interrupt */
|
||||
sdio_writeb(priv->func, val, addr, &ret);
|
||||
if (ret) {
|
||||
LOG_ERROR(priv, INIT, "Failure writing to "
|
||||
"Interrupt Enable Register (%d): %d\n", addr, ret);
|
||||
goto error_enable_int;
|
||||
}
|
||||
|
||||
sdio_release_host(func);
|
||||
|
||||
LOG_INFO(priv, INIT, "exit iwmct_probe\n");
|
||||
|
||||
return ret;
|
||||
|
||||
error_enable_int:
|
||||
sdio_release_irq(func);
|
||||
error_claim_irq:
|
||||
sdio_disable_func(func);
|
||||
error_dev_attrs:
|
||||
iwmct_dbgfs_unregister(priv->dbgfs);
|
||||
sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
|
||||
error_sdio_enable:
|
||||
sdio_release_host(func);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iwmct_remove(struct sdio_func *func)
|
||||
{
|
||||
struct iwmct_work_struct *read_req;
|
||||
struct iwmct_priv *priv = sdio_get_drvdata(func);
|
||||
|
||||
LOG_INFO(priv, INIT, "enter\n");
|
||||
|
||||
sdio_claim_host(func);
|
||||
sdio_release_irq(func);
|
||||
sdio_release_host(func);
|
||||
|
||||
/* Make sure works are finished */
|
||||
flush_work_sync(&priv->bus_rescan_worker);
|
||||
flush_work_sync(&priv->isr_worker);
|
||||
|
||||
sdio_claim_host(func);
|
||||
sdio_disable_func(func);
|
||||
sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
|
||||
iwmct_dbgfs_unregister(priv->dbgfs);
|
||||
sdio_release_host(func);
|
||||
|
||||
/* free read requests */
|
||||
while (!list_empty(&priv->read_req_list)) {
|
||||
read_req = list_entry(priv->read_req_list.next,
|
||||
struct iwmct_work_struct, list);
|
||||
|
||||
list_del(&read_req->list);
|
||||
kfree(read_req);
|
||||
}
|
||||
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
|
||||
static const struct sdio_device_id iwmct_ids[] = {
|
||||
/* Intel Wireless MultiCom 3200 Top Driver */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1404)},
|
||||
{ }, /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(sdio, iwmct_ids);
|
||||
|
||||
static struct sdio_driver iwmct_driver = {
|
||||
.probe = iwmct_probe,
|
||||
.remove = iwmct_remove,
|
||||
.name = DRV_NAME,
|
||||
.id_table = iwmct_ids,
|
||||
};
|
||||
|
||||
static int __init iwmct_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Default log filter settings */
|
||||
iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME);
|
||||
iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FW_FILTER_ALL);
|
||||
iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME);
|
||||
|
||||
rc = sdio_register_driver(&iwmct_driver);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit iwmct_exit(void)
|
||||
{
|
||||
sdio_unregister_driver(&iwmct_driver);
|
||||
}
|
||||
|
||||
module_init(iwmct_init);
|
||||
module_exit(iwmct_exit);
|
||||
|
@ -936,7 +936,7 @@ static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct cops_local *lp = netdev_priv(dev);
|
||||
struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr;
|
||||
struct atalk_addr *aa = (struct atalk_addr *)&lp->node_addr;
|
||||
struct atalk_addr *aa = &lp->node_addr;
|
||||
|
||||
switch(cmd)
|
||||
{
|
||||
|
@ -2454,24 +2454,27 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
|
||||
out:
|
||||
if (res) {
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave)
|
||||
int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave)
|
||||
{
|
||||
int ret = RX_HANDLER_ANOTHER;
|
||||
struct lacpdu *lacpdu, _lacpdu;
|
||||
|
||||
if (skb->protocol != PKT_TYPE_LACPDU)
|
||||
return ret;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct lacpdu)))
|
||||
lacpdu = skb_header_pointer(skb, 0, sizeof(_lacpdu), &_lacpdu);
|
||||
if (!lacpdu)
|
||||
return ret;
|
||||
|
||||
read_lock(&bond->lock);
|
||||
ret = bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len);
|
||||
ret = bond_3ad_rx_indication(lacpdu, slave, skb->len);
|
||||
read_unlock(&bond->lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -274,8 +274,8 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave);
|
||||
void bond_3ad_handle_link_change(struct slave *slave, char link);
|
||||
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info);
|
||||
int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev);
|
||||
int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave);
|
||||
int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave);
|
||||
int bond_3ad_set_carrier(struct bonding *bond);
|
||||
void bond_3ad_update_lacp_rate(struct bonding *bond);
|
||||
#endif //__BOND_3AD_H__
|
||||
|
@ -342,27 +342,17 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
}
|
||||
|
||||
static int rlb_arp_recv(struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave)
|
||||
static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave)
|
||||
{
|
||||
struct arp_pkt *arp;
|
||||
struct arp_pkt *arp, _arp;
|
||||
|
||||
if (skb->protocol != cpu_to_be16(ETH_P_ARP))
|
||||
goto out;
|
||||
|
||||
arp = (struct arp_pkt *) skb->data;
|
||||
if (!arp) {
|
||||
pr_debug("Packet has no ARP data\n");
|
||||
arp = skb_header_pointer(skb, 0, sizeof(_arp), &_arp);
|
||||
if (!arp)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, arp_hdr_len(bond->dev)))
|
||||
goto out;
|
||||
|
||||
if (skb->len < sizeof(struct arp_pkt)) {
|
||||
pr_debug("Packet is too small to be an ARP\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (arp->op_code == htons(ARPOP_REPLY)) {
|
||||
/* update rx hash table for this ARP */
|
||||
@ -1356,12 +1346,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
||||
}
|
||||
}
|
||||
|
||||
if (res) {
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
read_unlock(&bond->curr_slave_lock);
|
||||
|
||||
if (res) {
|
||||
/* no suitable interface, frame not sent */
|
||||
kfree_skb(skb);
|
||||
}
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
|
@ -395,8 +395,8 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
|
||||
skb->dev = slave_dev;
|
||||
|
||||
BUILD_BUG_ON(sizeof(skb->queue_mapping) !=
|
||||
sizeof(qdisc_skb_cb(skb)->bond_queue_mapping));
|
||||
skb->queue_mapping = qdisc_skb_cb(skb)->bond_queue_mapping;
|
||||
sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping));
|
||||
skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping;
|
||||
|
||||
if (unlikely(netpoll_tx_running(slave_dev)))
|
||||
bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb);
|
||||
@ -1240,9 +1240,7 @@ static inline int slave_enable_netpoll(struct slave *slave)
|
||||
if (!np)
|
||||
goto out;
|
||||
|
||||
np->dev = slave->dev;
|
||||
strlcpy(np->dev_name, slave->dev->name, IFNAMSIZ);
|
||||
err = __netpoll_setup(np);
|
||||
err = __netpoll_setup(np, slave->dev);
|
||||
if (err) {
|
||||
kfree(np);
|
||||
goto out;
|
||||
@ -1384,6 +1382,7 @@ static void bond_compute_features(struct bonding *bond)
|
||||
netdev_features_t vlan_features = BOND_VLAN_FEATURES;
|
||||
unsigned short max_hard_header_len = ETH_HLEN;
|
||||
int i;
|
||||
unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE;
|
||||
|
||||
read_lock(&bond->lock);
|
||||
|
||||
@ -1394,6 +1393,7 @@ static void bond_compute_features(struct bonding *bond)
|
||||
vlan_features = netdev_increment_features(vlan_features,
|
||||
slave->dev->vlan_features, BOND_VLAN_FEATURES);
|
||||
|
||||
dst_release_flag &= slave->dev->priv_flags;
|
||||
if (slave->dev->hard_header_len > max_hard_header_len)
|
||||
max_hard_header_len = slave->dev->hard_header_len;
|
||||
}
|
||||
@ -1402,6 +1402,9 @@ done:
|
||||
bond_dev->vlan_features = vlan_features;
|
||||
bond_dev->hard_header_len = max_hard_header_len;
|
||||
|
||||
flags = bond_dev->priv_flags & ~IFF_XMIT_DST_RELEASE;
|
||||
bond_dev->priv_flags = flags | dst_release_flag;
|
||||
|
||||
read_unlock(&bond->lock);
|
||||
|
||||
netdev_change_features(bond_dev);
|
||||
@ -1445,8 +1448,8 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct slave *slave;
|
||||
struct bonding *bond;
|
||||
int (*recv_probe)(struct sk_buff *, struct bonding *,
|
||||
struct slave *);
|
||||
int (*recv_probe)(const struct sk_buff *, struct bonding *,
|
||||
struct slave *);
|
||||
int ret = RX_HANDLER_ANOTHER;
|
||||
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
@ -1463,15 +1466,10 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
|
||||
|
||||
recv_probe = ACCESS_ONCE(bond->recv_probe);
|
||||
if (recv_probe) {
|
||||
struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
|
||||
|
||||
if (likely(nskb)) {
|
||||
ret = recv_probe(nskb, bond, slave);
|
||||
dev_kfree_skb(nskb);
|
||||
if (ret == RX_HANDLER_CONSUMED) {
|
||||
consume_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
ret = recv_probe(skb, bond, slave);
|
||||
if (ret == RX_HANDLER_CONSUMED) {
|
||||
consume_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2738,25 +2736,31 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
|
||||
}
|
||||
}
|
||||
|
||||
static int bond_arp_rcv(struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave)
|
||||
static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave)
|
||||
{
|
||||
struct arphdr *arp;
|
||||
struct arphdr *arp = (struct arphdr *)skb->data;
|
||||
unsigned char *arp_ptr;
|
||||
__be32 sip, tip;
|
||||
int alen;
|
||||
|
||||
if (skb->protocol != __cpu_to_be16(ETH_P_ARP))
|
||||
return RX_HANDLER_ANOTHER;
|
||||
|
||||
read_lock(&bond->lock);
|
||||
alen = arp_hdr_len(bond->dev);
|
||||
|
||||
pr_debug("bond_arp_rcv: bond %s skb->dev %s\n",
|
||||
bond->dev->name, skb->dev->name);
|
||||
|
||||
if (!pskb_may_pull(skb, arp_hdr_len(bond->dev)))
|
||||
goto out_unlock;
|
||||
if (alen > skb_headlen(skb)) {
|
||||
arp = kmalloc(alen, GFP_ATOMIC);
|
||||
if (!arp)
|
||||
goto out_unlock;
|
||||
if (skb_copy_bits(skb, 0, arp, alen) < 0)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
arp = arp_hdr(skb);
|
||||
if (arp->ar_hln != bond->dev->addr_len ||
|
||||
skb->pkt_type == PACKET_OTHERHOST ||
|
||||
skb->pkt_type == PACKET_LOOPBACK ||
|
||||
@ -2791,6 +2795,8 @@ static int bond_arp_rcv(struct sk_buff *skb, struct bonding *bond,
|
||||
|
||||
out_unlock:
|
||||
read_unlock(&bond->lock);
|
||||
if (arp != (struct arphdr *)skb->data)
|
||||
kfree(arp);
|
||||
return RX_HANDLER_ANOTHER;
|
||||
}
|
||||
|
||||
@ -3993,7 +3999,7 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
|
||||
out:
|
||||
if (res) {
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@ -4015,11 +4021,11 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
|
||||
res = bond_dev_queue_xmit(bond, skb,
|
||||
bond->curr_active_slave->dev);
|
||||
|
||||
read_unlock(&bond->curr_slave_lock);
|
||||
|
||||
if (res)
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
read_unlock(&bond->curr_slave_lock);
|
||||
kfree_skb(skb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
@ -4058,7 +4064,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
|
||||
|
||||
if (res) {
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@ -4096,7 +4102,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
|
||||
|
||||
res = bond_dev_queue_xmit(bond, skb2, tx_dev);
|
||||
if (res) {
|
||||
dev_kfree_skb(skb2);
|
||||
kfree_skb(skb2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -4110,7 +4116,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
|
||||
out:
|
||||
if (res)
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
|
||||
/* frame sent to all suitable interfaces */
|
||||
return NETDEV_TX_OK;
|
||||
@ -4178,7 +4184,7 @@ static u16 bond_select_queue(struct net_device *dev, struct sk_buff *skb)
|
||||
/*
|
||||
* Save the original txq to restore before passing to the driver
|
||||
*/
|
||||
qdisc_skb_cb(skb)->bond_queue_mapping = skb->queue_mapping;
|
||||
qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping;
|
||||
|
||||
if (unlikely(txq >= dev->real_num_tx_queues)) {
|
||||
do {
|
||||
@ -4216,7 +4222,7 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
|
||||
pr_err("%s: Error: Unknown bonding mode %d\n",
|
||||
dev->name, bond->params.mode);
|
||||
WARN_ON_ONCE(1);
|
||||
dev_kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
}
|
||||
@ -4238,7 +4244,7 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
if (bond->slave_cnt)
|
||||
ret = __bond_start_xmit(skb, dev);
|
||||
else
|
||||
dev_kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
|
||||
read_unlock(&bond->lock);
|
||||
|
||||
@ -4839,17 +4845,19 @@ static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bond_get_tx_queues(struct net *net, struct nlattr *tb[])
|
||||
static unsigned int bond_get_num_tx_queues(void)
|
||||
{
|
||||
return tx_queues;
|
||||
}
|
||||
|
||||
static struct rtnl_link_ops bond_link_ops __read_mostly = {
|
||||
.kind = "bond",
|
||||
.priv_size = sizeof(struct bonding),
|
||||
.setup = bond_setup,
|
||||
.validate = bond_validate,
|
||||
.get_tx_queues = bond_get_tx_queues,
|
||||
.kind = "bond",
|
||||
.priv_size = sizeof(struct bonding),
|
||||
.setup = bond_setup,
|
||||
.validate = bond_validate,
|
||||
.get_num_tx_queues = bond_get_num_tx_queues,
|
||||
.get_num_rx_queues = bond_get_num_tx_queues, /* Use the same number
|
||||
as for TX queues */
|
||||
};
|
||||
|
||||
/* Create a new bond based on the specified name and bonding parameters.
|
||||
|
@ -1495,7 +1495,7 @@ static ssize_t bonding_store_queue_id(struct device *d,
|
||||
/* Check buffer length, valid ifname and queue id */
|
||||
if (strlen(buffer) > IFNAMSIZ ||
|
||||
!dev_valid_name(buffer) ||
|
||||
qid > bond->params.tx_queues)
|
||||
qid > bond->dev->real_num_tx_queues)
|
||||
goto err_no_cmd;
|
||||
|
||||
/* Get the pointer to that interface if it exists */
|
||||
|
@ -218,8 +218,8 @@ struct bonding {
|
||||
struct slave *primary_slave;
|
||||
bool force_primary;
|
||||
s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
|
||||
int (*recv_probe)(struct sk_buff *, struct bonding *,
|
||||
struct slave *);
|
||||
int (*recv_probe)(const struct sk_buff *, struct bonding *,
|
||||
struct slave *);
|
||||
rwlock_t lock;
|
||||
rwlock_t curr_slave_lock;
|
||||
u8 send_peer_notif;
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user