mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 01:40:53 +07:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Highlights (1721 non-merge commits, this has to be a record of some sort): 1) Add 'random' mode to team driver, from Jiri Pirko and Eric Dumazet. 2) Make it so that any driver that supports configuration of multiple MAC addresses can provide the forwarding database add and del calls by providing a default implementation and hooking that up if the driver doesn't have an explicit set of handlers. From Vlad Yasevich. 3) Support GSO segmentation over tunnels and other encapsulating devices such as VXLAN, from Pravin B Shelar. 4) Support L2 GRE tunnels in the flow dissector, from Michael Dalton. 5) Implement Tail Loss Probe (TLP) detection in TCP, from Nandita Dukkipati. 6) In the PHY layer, allow supporting wake-on-lan in situations where the PHY registers have to be written for it to be configured. Use it to support wake-on-lan in mv643xx_eth. From Michael Stapelberg. 7) Significantly improve firewire IPV6 support, from YOSHIFUJI Hideaki. 8) Allow multiple packets to be sent in a single transmission using network coding in batman-adv, from Martin Hundebøll. 9) Add support for T5 cxgb4 chips, from Santosh Rastapur. 10) Generalize the VXLAN forwarding tables so that there is more flexibility in configurating various aspects of the endpoints. From David Stevens. 11) Support RSS and TSO in hardware over GRE tunnels in bxn2x driver, from Dmitry Kravkov. 12) Zero copy support in nfnelink_queue, from Eric Dumazet and Pablo Neira Ayuso. 13) Start adding networking selftests. 14) In situations of overload on the same AF_PACKET fanout socket, or per-cpu packet receive queue, minimize drop by distributing the load to other cpus/fanouts. From Willem de Bruijn and Eric Dumazet. 15) Add support for new payload offset BPF instruction, from Daniel Borkmann. 16) Convert several drivers over to mdoule_platform_driver(), from Sachin Kamat. 17) Provide a minimal BPF JIT image disassembler userspace tool, from Daniel Borkmann. 18) Rewrite F-RTO implementation in TCP to match the final specification of it in RFC4138 and RFC5682. From Yuchung Cheng. 19) Provide netlink socket diag of netlink sockets ("Yo dawg, I hear you like netlink, so I implemented netlink dumping of netlink sockets.") From Andrey Vagin. 20) Remove ugly passing of rtnetlink attributes into rtnl_doit functions, from Thomas Graf. 21) Allow userspace to be able to see if a configuration change occurs in the middle of an address or device list dump, from Nicolas Dichtel. 22) Support RFC3168 ECN protection for ipv6 fragments, from Hannes Frederic Sowa. 23) Increase accuracy of packet length used by packet scheduler, from Jason Wang. 24) Beginning set of changes to make ipv4/ipv6 fragment handling more scalable and less susceptible to overload and locking contention, from Jesper Dangaard Brouer. 25) Get rid of using non-type-safe NLMSG_* macros and use nlmsg_*() instead. From Hong Zhiguo. 26) Optimize route usage in IPVS by avoiding reference counting where possible, from Julian Anastasov. 27) Convert IPVS schedulers to RCU, also from Julian Anastasov. 28) Support cpu fanouts in xt_NFQUEUE netfilter target, from Holger Eitzenberger. 29) Network namespace support for nf_log, ebt_log, xt_LOG, ipt_ULOG, nfnetlink_log, and nfnetlink_queue. From Gao feng. 30) Implement RFC3168 ECN protection, from Hannes Frederic Sowa. 31) Support several new r8169 chips, from Hayes Wang. 32) Support tokenized interface identifiers in ipv6, from Daniel Borkmann. 33) Use usbnet_link_change() helper in USB net driver, from Ming Lei. 34) Add 802.1ad vlan offload support, from Patrick McHardy. 35) Support mmap() based netlink communication, also from Patrick McHardy. 36) Support HW timestamping in mlx4 driver, from Amir Vadai. 37) Rationalize AF_PACKET packet timestamping when transmitting, from Willem de Bruijn and Daniel Borkmann. 38) Bring parity to what's provided by /proc/net/packet socket dumping and the info provided by netlink socket dumping of AF_PACKET sockets. From Nicolas Dichtel. 39) Fix peeking beyond zero sized SKBs in AF_UNIX, from Benjamin Poirier" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1722 commits) filter: fix va_list build error af_unix: fix a fatal race with bit fields bnx2x: Prevent memory leak when cnic is absent bnx2x: correct reading of speed capabilities net: sctp: attribute printl with __printf for gcc fmt checks netlink: kconfig: move mmap i/o into netlink kconfig netpoll: convert mutex into a semaphore netlink: Fix skb ref counting. net_sched: act_ipt forward compat with xtables mlx4_en: fix a build error on 32bit arches Revert "bnx2x: allow nvram test to run when device is down" bridge: avoid OOPS if root port not found drivers: net: cpsw: fix kernel warn on cpsw irq enable sh_eth: use random MAC address if no valid one supplied 3c509.c: call SET_NETDEV_DEV for all device types (ISA/ISAPnP/EISA) tg3: fix to append hardware time stamping flags unix/stream: fix peeking with an offset larger than data in queue unix/dgram: fix peeking with an offset larger than data in queue unix/dgram: peek beyond 0-sized skbs openvswitch: Remove unneeded ovs_netdev_get_ifindex() ...
This commit is contained in:
commit
73287a43cc
@ -67,6 +67,14 @@ Description:
|
||||
Defines the penalty which will be applied to an
|
||||
originator message's tq-field on every hop.
|
||||
|
||||
What: /sys/class/net/<mesh_iface>/mesh/network_coding
|
||||
Date: Nov 2012
|
||||
Contact: Martin Hundeboll <martin@hundeboll.net>
|
||||
Description:
|
||||
Controls whether Network Coding (using some magic
|
||||
to send fewer wifi packets but still the same
|
||||
content) is enabled or not.
|
||||
|
||||
What: /sys/class/net/<mesh_iface>/mesh/orig_interval
|
||||
Date: May 2010
|
||||
Contact: Marek Lindner <lindner_marek@yahoo.de>
|
||||
|
@ -437,7 +437,7 @@
|
||||
</section>
|
||||
!Finclude/net/mac80211.h ieee80211_get_buffered_bc
|
||||
!Finclude/net/mac80211.h ieee80211_beacon_get
|
||||
!Finclude/net/mac80211.h ieee80211_sta_eosp_irqsafe
|
||||
!Finclude/net/mac80211.h ieee80211_sta_eosp
|
||||
!Finclude/net/mac80211.h ieee80211_frame_release_type
|
||||
!Finclude/net/mac80211.h ieee80211_sta_ps_transition
|
||||
!Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni
|
||||
|
@ -18,6 +18,8 @@ memcg_test.txt
|
||||
- Memory Resource Controller; implementation details.
|
||||
memory.txt
|
||||
- Memory Resource Controller; design, accounting, interface, testing.
|
||||
net_cls.txt
|
||||
- Network classifier cgroups details and usages.
|
||||
net_prio.txt
|
||||
- Network priority cgroups details and usages.
|
||||
resource_counter.txt
|
||||
|
34
Documentation/cgroups/net_cls.txt
Normal file
34
Documentation/cgroups/net_cls.txt
Normal file
@ -0,0 +1,34 @@
|
||||
Network classifier cgroup
|
||||
-------------------------
|
||||
|
||||
The Network classifier cgroup provides an interface to
|
||||
tag network packets with a class identifier (classid).
|
||||
|
||||
The Traffic Controller (tc) can be used to assign
|
||||
different priorities to packets from different cgroups.
|
||||
|
||||
Creating a net_cls cgroups instance creates a net_cls.classid file.
|
||||
This net_cls.classid value is initialized to 0.
|
||||
|
||||
You can write hexadecimal values to net_cls.classid; the format for these
|
||||
values is 0xAAAABBBB; AAAA is the major handle number and BBBB
|
||||
is the minor handle number.
|
||||
Reading net_cls.classid yields a decimal result.
|
||||
|
||||
Example:
|
||||
mkdir /sys/fs/cgroup/net_cls
|
||||
mount -t cgroup -onet_cls net_cls /sys/fs/cgroup/net_cls
|
||||
mkdir /sys/fs/cgroup/net_cls/0
|
||||
echo 0x100001 > /sys/fs/cgroup/net_cls/0/net_cls.classid
|
||||
- setting a 10:1 handle.
|
||||
|
||||
cat /sys/fs/cgroup/net_cls/0/net_cls.classid
|
||||
1048577
|
||||
|
||||
configuring tc:
|
||||
tc qdisc add dev eth0 root handle 10: htb
|
||||
|
||||
tc class add dev eth0 parent 10: classid 10:1 htb rate 40mbit
|
||||
- creating traffic class 10:1
|
||||
|
||||
tc filter add dev eth0 parent 10: protocol ip prio 10 handle 1: cgroup
|
@ -115,6 +115,9 @@ prefixed with the string "marvell,", for Marvell Technology Group Ltd.
|
||||
- compatible : "marvell,mv64360-eth-block"
|
||||
- reg : Offset and length of the register set for this block
|
||||
|
||||
Optional properties:
|
||||
- clocks : Phandle to the clock control device and gate bit
|
||||
|
||||
Example Discovery Ethernet block node:
|
||||
ethernet-block@2000 {
|
||||
#address-cells = <1>;
|
||||
|
14
Documentation/devicetree/bindings/net/can/atmel-can.txt
Normal file
14
Documentation/devicetree/bindings/net/can/atmel-can.txt
Normal file
@ -0,0 +1,14 @@
|
||||
* AT91 CAN *
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "atmel,at91sam9263-can" or "atmel,at91sam9x5-can"
|
||||
- reg: Should contain CAN controller registers location and length
|
||||
- interrupts: Should contain IRQ line for the CAN controller
|
||||
|
||||
Example:
|
||||
|
||||
can0: can@f000c000 {
|
||||
compatbile = "atmel,at91sam9x5-can";
|
||||
reg = <0xf000c000 0x300>;
|
||||
interrupts = <40 4 5>
|
||||
};
|
@ -15,16 +15,22 @@ Required properties:
|
||||
- mac_control : Specifies Default MAC control register content
|
||||
for the specific platform
|
||||
- slaves : Specifies number for slaves
|
||||
- cpts_active_slave : Specifies the slave to use for time stamping
|
||||
- active_slave : Specifies the slave to use for time stamping,
|
||||
ethtool and SIOCGMIIPHY
|
||||
- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds
|
||||
- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds
|
||||
- phy_id : Specifies slave phy id
|
||||
- mac-address : Specifies slave MAC address
|
||||
|
||||
Optional properties:
|
||||
- ti,hwmods : Must be "cpgmac0"
|
||||
- no_bd_ram : Must be 0 or 1
|
||||
- dual_emac : Specifies Switch to act as Dual EMAC
|
||||
|
||||
Slave Properties:
|
||||
Required properties:
|
||||
- phy_id : Specifies slave phy id
|
||||
- mac-address : Specifies slave MAC address
|
||||
|
||||
Optional properties:
|
||||
- dual_emac_res_vlan : Specifies VID to be used to segregate the ports
|
||||
|
||||
Note: "ti,hwmods" field is used to fetch the base address and irq
|
||||
@ -47,7 +53,7 @@ Examples:
|
||||
rx_descs = <64>;
|
||||
mac_control = <0x20>;
|
||||
slaves = <2>;
|
||||
cpts_active_slave = <0>;
|
||||
active_slave = <0>;
|
||||
cpts_clock_mult = <0x80000000>;
|
||||
cpts_clock_shift = <29>;
|
||||
cpsw_emac0: slave@0 {
|
||||
@ -73,7 +79,7 @@ Examples:
|
||||
rx_descs = <64>;
|
||||
mac_control = <0x20>;
|
||||
slaves = <2>;
|
||||
cpts_active_slave = <0>;
|
||||
active_slave = <0>;
|
||||
cpts_clock_mult = <0x80000000>;
|
||||
cpts_clock_shift = <29>;
|
||||
cpsw_emac0: slave@0 {
|
||||
|
91
Documentation/devicetree/bindings/net/dsa/dsa.txt
Normal file
91
Documentation/devicetree/bindings/net/dsa/dsa.txt
Normal file
@ -0,0 +1,91 @@
|
||||
Marvell Distributed Switch Architecture Device Tree Bindings
|
||||
------------------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "marvell,dsa"
|
||||
- #address-cells : Must be 2, first cell is the address on the MDIO bus
|
||||
and second cell is the address in the switch tree.
|
||||
Second cell is used only when cascading/chaining.
|
||||
- #size-cells : Must be 0
|
||||
- dsa,ethernet : Should be a phandle to a valid Ethernet device node
|
||||
- dsa,mii-bus : Should be a phandle to a valid MDIO bus device node
|
||||
|
||||
Optionnal properties:
|
||||
- interrupts : property with a value describing the switch
|
||||
interrupt number (not supported by the driver)
|
||||
|
||||
A DSA node can contain multiple switch chips which are therefore child nodes of
|
||||
the parent DSA node. The maximum number of allowed child nodes is 4
|
||||
(DSA_MAX_SWITCHES).
|
||||
Each of these switch child nodes should have the following required properties:
|
||||
|
||||
- reg : Describes the switch address on the MII bus
|
||||
- #address-cells : Must be 1
|
||||
- #size-cells : Must be 0
|
||||
|
||||
A switch may have multiple "port" children nodes
|
||||
|
||||
Each port children node must have the following mandatory properties:
|
||||
- reg : Describes the port address in the switch
|
||||
- label : Describes the label associated with this port, special
|
||||
labels are "cpu" to indicate a CPU port and "dsa" to
|
||||
indicate an uplink/downlink port.
|
||||
|
||||
Note that a port labelled "dsa" will imply checking for the uplink phandle
|
||||
described below.
|
||||
|
||||
Optionnal property:
|
||||
- link : Should be a phandle to another switch's DSA port.
|
||||
This property is only used when switches are being
|
||||
chained/cascaded together.
|
||||
|
||||
Example:
|
||||
|
||||
dsa@0 {
|
||||
compatible = "marvell,dsa";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
|
||||
interrupts = <10>;
|
||||
dsa,ethernet = <ðernet0>;
|
||||
dsa,mii-bus = <&mii_bus0>;
|
||||
|
||||
switch@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <16 0>; /* MDIO address 16, switch 0 in tree */
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan1";
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan2";
|
||||
};
|
||||
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "cpu";
|
||||
};
|
||||
|
||||
switch0uplink: port@6 {
|
||||
reg = <6>;
|
||||
label = "dsa";
|
||||
link = <&switch1uplink>;
|
||||
};
|
||||
};
|
||||
|
||||
switch@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <17 1>; /* MDIO address 17, switch 1 in tree */
|
||||
|
||||
switch1uplink: port@0 {
|
||||
reg = <0>;
|
||||
label = "dsa";
|
||||
link = <&switch0uplink>;
|
||||
};
|
||||
};
|
||||
};
|
@ -9,6 +9,10 @@ Required properties:
|
||||
- compatible: "marvell,orion-mdio"
|
||||
- reg: address and length of the SMI register
|
||||
|
||||
Optional properties:
|
||||
- interrupts: interrupt line number for the SMI error/done interrupt
|
||||
- clocks: Phandle to the clock control device and gate bit
|
||||
|
||||
The child nodes of the MDIO driver are the individual PHY devices
|
||||
connected to this MDIO bus. They must have a "reg" property given the
|
||||
PHY address on the MDIO bus.
|
||||
|
@ -71,8 +71,9 @@ submits skb to qdisc), so if you need something from that cb later, you should
|
||||
store info in the skb->data on your own.
|
||||
|
||||
To hook the MLME interface you have to populate the ml_priv field of your
|
||||
net_device with a pointer to struct ieee802154_mlme_ops instance. All fields are
|
||||
required.
|
||||
net_device with a pointer to struct ieee802154_mlme_ops instance. The fields
|
||||
assoc_req, assoc_resp, disassoc_req, start_req, and scan_req are optional.
|
||||
All other fields are required.
|
||||
|
||||
We provide an example of simple HardMAC driver at drivers/ieee802154/fakehard.c
|
||||
|
||||
|
@ -29,7 +29,7 @@ route/max_size - INTEGER
|
||||
neigh/default/gc_thresh1 - INTEGER
|
||||
Minimum number of entries to keep. Garbage collector will not
|
||||
purge entries if there are fewer than this number.
|
||||
Default: 256
|
||||
Default: 128
|
||||
|
||||
neigh/default/gc_thresh3 - INTEGER
|
||||
Maximum number of neighbor entries allowed. Increase this
|
||||
@ -175,14 +175,6 @@ tcp_congestion_control - STRING
|
||||
is inherited.
|
||||
[see setsockopt(listenfd, SOL_TCP, TCP_CONGESTION, "name" ...) ]
|
||||
|
||||
tcp_cookie_size - INTEGER
|
||||
Default size of TCP Cookie Transactions (TCPCT) option, that may be
|
||||
overridden on a per socket basis by the TCPCT socket option.
|
||||
Values greater than the maximum (16) are interpreted as the maximum.
|
||||
Values greater than zero and less than the minimum (8) are interpreted
|
||||
as the minimum. Odd values are interpreted as the next even value.
|
||||
Default: 0 (off).
|
||||
|
||||
tcp_dsack - BOOLEAN
|
||||
Allows TCP to send "duplicate" SACKs.
|
||||
|
||||
@ -190,7 +182,9 @@ tcp_early_retrans - INTEGER
|
||||
Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold
|
||||
for triggering fast retransmit when the amount of outstanding data is
|
||||
small and when no previously unsent data can be transmitted (such
|
||||
that limited transmit could be used).
|
||||
that limited transmit could be used). Also controls the use of
|
||||
Tail loss probe (TLP) that converts RTOs occuring due to tail
|
||||
losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01).
|
||||
Possible values:
|
||||
0 disables ER
|
||||
1 enables ER
|
||||
@ -198,7 +192,9 @@ tcp_early_retrans - INTEGER
|
||||
by a fourth of RTT. This mitigates connection falsely
|
||||
recovers when network has a small degree of reordering
|
||||
(less than 3 packets).
|
||||
Default: 2
|
||||
3 enables delayed ER and TLP.
|
||||
4 enables TLP only.
|
||||
Default: 3
|
||||
|
||||
tcp_ecn - INTEGER
|
||||
Control use of Explicit Congestion Notification (ECN) by TCP.
|
||||
@ -229,36 +225,13 @@ tcp_fin_timeout - INTEGER
|
||||
Default: 60 seconds
|
||||
|
||||
tcp_frto - INTEGER
|
||||
Enables Forward RTO-Recovery (F-RTO) defined in RFC4138.
|
||||
Enables Forward RTO-Recovery (F-RTO) defined in RFC5682.
|
||||
F-RTO is an enhanced recovery algorithm for TCP retransmission
|
||||
timeouts. It is particularly beneficial in wireless environments
|
||||
where packet loss is typically due to random radio interference
|
||||
rather than intermediate router congestion. F-RTO is sender-side
|
||||
only modification. Therefore it does not require any support from
|
||||
the peer.
|
||||
timeouts. It is particularly beneficial in networks where the
|
||||
RTT fluctuates (e.g., wireless). F-RTO is sender-side only
|
||||
modification. It does not require any support from the peer.
|
||||
|
||||
If set to 1, basic version is enabled. 2 enables SACK enhanced
|
||||
F-RTO if flow uses SACK. The basic version can be used also when
|
||||
SACK is in use though scenario(s) with it exists where F-RTO
|
||||
interacts badly with the packet counting of the SACK enabled TCP
|
||||
flow.
|
||||
|
||||
tcp_frto_response - INTEGER
|
||||
When F-RTO has detected that a TCP retransmission timeout was
|
||||
spurious (i.e, the timeout would have been avoided had TCP set a
|
||||
longer retransmission timeout), TCP has several options what to do
|
||||
next. Possible values are:
|
||||
0 Rate halving based; a smooth and conservative response,
|
||||
results in halved cwnd and ssthresh after one RTT
|
||||
1 Very conservative response; not recommended because even
|
||||
though being valid, it interacts poorly with the rest of
|
||||
Linux TCP, halves cwnd and ssthresh immediately
|
||||
2 Aggressive response; undoes congestion control measures
|
||||
that are now known to be unnecessary (ignoring the
|
||||
possibility of a lost retransmission that would require
|
||||
TCP to be more cautious), cwnd and ssthresh are restored
|
||||
to the values prior timeout
|
||||
Default: 0 (rate halving based)
|
||||
By default it's enabled with a non-zero value. 0 disables F-RTO.
|
||||
|
||||
tcp_keepalive_time - INTEGER
|
||||
How often TCP sends out keepalive messages when keepalive is enabled.
|
||||
|
339
Documentation/networking/netlink_mmap.txt
Normal file
339
Documentation/networking/netlink_mmap.txt
Normal file
@ -0,0 +1,339 @@
|
||||
This file documents how to use memory mapped I/O with netlink.
|
||||
|
||||
Author: Patrick McHardy <kaber@trash.net>
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Memory mapped netlink I/O can be used to increase throughput and decrease
|
||||
overhead of unicast receive and transmit operations. Some netlink subsystems
|
||||
require high throughput, these are mainly the netfilter subsystems
|
||||
nfnetlink_queue and nfnetlink_log, but it can also help speed up large
|
||||
dump operations of f.i. the routing database.
|
||||
|
||||
Memory mapped netlink I/O used two circular ring buffers for RX and TX which
|
||||
are mapped into the processes address space.
|
||||
|
||||
The RX ring is used by the kernel to directly construct netlink messages into
|
||||
user-space memory without copying them as done with regular socket I/O,
|
||||
additionally as long as the ring contains messages no recvmsg() or poll()
|
||||
syscalls have to be issued by user-space to get more message.
|
||||
|
||||
The TX ring is used to process messages directly from user-space memory, the
|
||||
kernel processes all messages contained in the ring using a single sendmsg()
|
||||
call.
|
||||
|
||||
Usage overview
|
||||
--------------
|
||||
|
||||
In order to use memory mapped netlink I/O, user-space needs three main changes:
|
||||
|
||||
- ring setup
|
||||
- conversion of the RX path to get messages from the ring instead of recvmsg()
|
||||
- conversion of the TX path to construct messages into the ring
|
||||
|
||||
Ring setup is done using setsockopt() to provide the ring parameters to the
|
||||
kernel, then a call to mmap() to map the ring into the processes address space:
|
||||
|
||||
- setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, ¶ms, sizeof(params));
|
||||
- setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, ¶ms, sizeof(params));
|
||||
- ring = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
|
||||
|
||||
Usage of either ring is optional, but even if only the RX ring is used the
|
||||
mapping still needs to be writable in order to update the frame status after
|
||||
processing.
|
||||
|
||||
Conversion of the reception path involves calling poll() on the file
|
||||
descriptor, once the socket is readable the frames from the ring are
|
||||
processsed in order until no more messages are available, as indicated by
|
||||
a status word in the frame header.
|
||||
|
||||
On kernel side, in order to make use of memory mapped I/O on receive, the
|
||||
originating netlink subsystem needs to support memory mapped I/O, otherwise
|
||||
it will use an allocated socket buffer as usual and the contents will be
|
||||
copied to the ring on transmission, nullifying most of the performance gains.
|
||||
Dumps of kernel databases automatically support memory mapped I/O.
|
||||
|
||||
Conversion of the transmit path involves changing message contruction to
|
||||
use memory from the TX ring instead of (usually) a buffer declared on the
|
||||
stack and setting up the frame header approriately. Optionally poll() can
|
||||
be used to wait for free frames in the TX ring.
|
||||
|
||||
Structured and definitions for using memory mapped I/O are contained in
|
||||
<linux/netlink.h>.
|
||||
|
||||
RX and TX rings
|
||||
----------------
|
||||
|
||||
Each ring contains a number of continous memory blocks, containing frames of
|
||||
fixed size dependant on the parameters used for ring setup.
|
||||
|
||||
Ring: [ block 0 ]
|
||||
[ frame 0 ]
|
||||
[ frame 1 ]
|
||||
[ block 1 ]
|
||||
[ frame 2 ]
|
||||
[ frame 3 ]
|
||||
...
|
||||
[ block n ]
|
||||
[ frame 2 * n ]
|
||||
[ frame 2 * n + 1 ]
|
||||
|
||||
The blocks are only visible to the kernel, from the point of view of user-space
|
||||
the ring just contains the frames in a continous memory zone.
|
||||
|
||||
The ring parameters used for setting up the ring are defined as follows:
|
||||
|
||||
struct nl_mmap_req {
|
||||
unsigned int nm_block_size;
|
||||
unsigned int nm_block_nr;
|
||||
unsigned int nm_frame_size;
|
||||
unsigned int nm_frame_nr;
|
||||
};
|
||||
|
||||
Frames are grouped into blocks, where each block is a continous region of memory
|
||||
and holds nm_block_size / nm_frame_size frames. The total number of frames in
|
||||
the ring is nm_frame_nr. The following invariants hold:
|
||||
|
||||
- frames_per_block = nm_block_size / nm_frame_size
|
||||
|
||||
- nm_frame_nr = frames_per_block * nm_block_nr
|
||||
|
||||
Some parameters are constrained, specifically:
|
||||
|
||||
- nm_block_size must be a multiple of the architectures memory page size.
|
||||
The getpagesize() function can be used to get the page size.
|
||||
|
||||
- nm_frame_size must be equal or larger to NL_MMAP_HDRLEN, IOW a frame must be
|
||||
able to hold at least the frame header
|
||||
|
||||
- nm_frame_size must be smaller or equal to nm_block_size
|
||||
|
||||
- nm_frame_size must be a multiple of NL_MMAP_MSG_ALIGNMENT
|
||||
|
||||
- nm_frame_nr must equal the actual number of frames as specified above.
|
||||
|
||||
When the kernel can't allocate phsyically continous memory for a ring block,
|
||||
it will fall back to use physically discontinous memory. This might affect
|
||||
performance negatively, in order to avoid this the nm_frame_size parameter
|
||||
should be chosen to be as small as possible for the required frame size and
|
||||
the number of blocks should be increased instead.
|
||||
|
||||
Ring frames
|
||||
------------
|
||||
|
||||
Each frames contain a frame header, consisting of a synchronization word and some
|
||||
meta-data, and the message itself.
|
||||
|
||||
Frame: [ header message ]
|
||||
|
||||
The frame header is defined as follows:
|
||||
|
||||
struct nl_mmap_hdr {
|
||||
unsigned int nm_status;
|
||||
unsigned int nm_len;
|
||||
__u32 nm_group;
|
||||
/* credentials */
|
||||
__u32 nm_pid;
|
||||
__u32 nm_uid;
|
||||
__u32 nm_gid;
|
||||
};
|
||||
|
||||
- nm_status is used for synchronizing processing between the kernel and user-
|
||||
space and specifies ownership of the frame as well as the operation to perform
|
||||
|
||||
- nm_len contains the length of the message contained in the data area
|
||||
|
||||
- nm_group specified the destination multicast group of message
|
||||
|
||||
- nm_pid, nm_uid and nm_gid contain the netlink pid, UID and GID of the sending
|
||||
process. These values correspond to the data available using SOCK_PASSCRED in
|
||||
the SCM_CREDENTIALS cmsg.
|
||||
|
||||
The possible values in the status word are:
|
||||
|
||||
- NL_MMAP_STATUS_UNUSED:
|
||||
RX ring: frame belongs to the kernel and contains no message
|
||||
for user-space. Approriate action is to invoke poll()
|
||||
to wait for new messages.
|
||||
|
||||
TX ring: frame belongs to user-space and can be used for
|
||||
message construction.
|
||||
|
||||
- NL_MMAP_STATUS_RESERVED:
|
||||
RX ring only: frame is currently used by the kernel for message
|
||||
construction and contains no valid message yet.
|
||||
Appropriate action is to invoke poll() to wait for
|
||||
new messages.
|
||||
|
||||
- NL_MMAP_STATUS_VALID:
|
||||
RX ring: frame contains a valid message. Approriate action is
|
||||
to process the message and release the frame back to
|
||||
the kernel by setting the status to
|
||||
NL_MMAP_STATUS_UNUSED or queue the frame by setting the
|
||||
status to NL_MMAP_STATUS_SKIP.
|
||||
|
||||
TX ring: the frame contains a valid message from user-space to
|
||||
be processed by the kernel. After completing processing
|
||||
the kernel will release the frame back to user-space by
|
||||
setting the status to NL_MMAP_STATUS_UNUSED.
|
||||
|
||||
- NL_MMAP_STATUS_COPY:
|
||||
RX ring only: a message is ready to be processed but could not be
|
||||
stored in the ring, either because it exceeded the
|
||||
frame size or because the originating subsystem does
|
||||
not support memory mapped I/O. Appropriate action is
|
||||
to invoke recvmsg() to receive the message and release
|
||||
the frame back to the kernel by setting the status to
|
||||
NL_MMAP_STATUS_UNUSED.
|
||||
|
||||
- NL_MMAP_STATUS_SKIP:
|
||||
RX ring only: user-space queued the message for later processing, but
|
||||
processed some messages following it in the ring. The
|
||||
kernel should skip this frame when looking for unused
|
||||
frames.
|
||||
|
||||
The data area of a frame begins at a offset of NL_MMAP_HDRLEN relative to the
|
||||
frame header.
|
||||
|
||||
TX limitations
|
||||
--------------
|
||||
|
||||
Kernel processing usually involves validation of the message received by
|
||||
user-space, then processing its contents. The kernel must assure that
|
||||
userspace is not able to modify the message contents after they have been
|
||||
validated. In order to do so, the message is copied from the ring frame
|
||||
to an allocated buffer if either of these conditions is false:
|
||||
|
||||
- only a single mapping of the ring exists
|
||||
- the file descriptor is not shared between processes
|
||||
|
||||
This means that for threaded programs, the kernel will fall back to copying.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Ring setup:
|
||||
|
||||
unsigned int block_size = 16 * getpagesize();
|
||||
struct nl_mmap_req req = {
|
||||
.nm_block_size = block_size,
|
||||
.nm_block_nr = 64,
|
||||
.nm_frame_size = 16384,
|
||||
.nm_frame_nr = 64 * block_size / 16384,
|
||||
};
|
||||
unsigned int ring_size;
|
||||
void *rx_ring, *tx_ring;
|
||||
|
||||
/* Configure ring parameters */
|
||||
if (setsockopt(fd, NETLINK_RX_RING, &req, sizeof(req)) < 0)
|
||||
exit(1);
|
||||
if (setsockopt(fd, NETLINK_TX_RING, &req, sizeof(req)) < 0)
|
||||
exit(1)
|
||||
|
||||
/* Calculate size of each invididual ring */
|
||||
ring_size = req.nm_block_nr * req.nm_block_size;
|
||||
|
||||
/* Map RX/TX rings. The TX ring is located after the RX ring */
|
||||
rx_ring = mmap(NULL, 2 * ring_size, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fd, 0);
|
||||
if ((long)rx_ring == -1L)
|
||||
exit(1);
|
||||
tx_ring = rx_ring + ring_size:
|
||||
|
||||
Message reception:
|
||||
|
||||
This example assumes some ring parameters of the ring setup are available.
|
||||
|
||||
unsigned int frame_offset = 0;
|
||||
struct nl_mmap_hdr *hdr;
|
||||
struct nlmsghdr *nlh;
|
||||
unsigned char buf[16384];
|
||||
ssize_t len;
|
||||
|
||||
while (1) {
|
||||
struct pollfd pfds[1];
|
||||
|
||||
pfds[0].fd = fd;
|
||||
pfds[0].events = POLLIN | POLLERR;
|
||||
pfds[0].revents = 0;
|
||||
|
||||
if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
|
||||
exit(1);
|
||||
|
||||
/* Check for errors. Error handling omitted */
|
||||
if (pfds[0].revents & POLLERR)
|
||||
<handle error>
|
||||
|
||||
/* If no new messages, poll again */
|
||||
if (!(pfds[0].revents & POLLIN))
|
||||
continue;
|
||||
|
||||
/* Process all frames */
|
||||
while (1) {
|
||||
/* Get next frame header */
|
||||
hdr = rx_ring + frame_offset;
|
||||
|
||||
if (hdr->nm_status == NL_MMAP_STATUS_VALID)
|
||||
/* Regular memory mapped frame */
|
||||
nlh = (void *hdr) + NL_MMAP_HDRLEN;
|
||||
len = hdr->nm_len;
|
||||
|
||||
/* Release empty message immediately. May happen
|
||||
* on error during message construction.
|
||||
*/
|
||||
if (len == 0)
|
||||
goto release;
|
||||
} else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
|
||||
/* Frame queued to socket receive queue */
|
||||
len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (len <= 0)
|
||||
break;
|
||||
nlh = buf;
|
||||
} else
|
||||
/* No more messages to process, continue polling */
|
||||
break;
|
||||
|
||||
process_msg(nlh);
|
||||
release:
|
||||
/* Release frame back to the kernel */
|
||||
hdr->nm_status = NL_MMAP_STATUS_UNUSED;
|
||||
|
||||
/* Advance frame offset to next frame */
|
||||
frame_offset = (frame_offset + frame_size) % ring_size;
|
||||
}
|
||||
}
|
||||
|
||||
Message transmission:
|
||||
|
||||
This example assumes some ring parameters of the ring setup are available.
|
||||
A single message is constructed and transmitted, to send multiple messages
|
||||
at once they would be constructed in consecutive frames before a final call
|
||||
to sendto().
|
||||
|
||||
unsigned int frame_offset = 0;
|
||||
struct nl_mmap_hdr *hdr;
|
||||
struct nlmsghdr *nlh;
|
||||
struct sockaddr_nl addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
};
|
||||
|
||||
hdr = tx_ring + frame_offset;
|
||||
if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
|
||||
/* No frame available. Use poll() to avoid. */
|
||||
exit(1);
|
||||
|
||||
nlh = (void *)hdr + NL_MMAP_HDRLEN;
|
||||
|
||||
/* Build message */
|
||||
build_message(nlh);
|
||||
|
||||
/* Fill frame header: length and status need to be set */
|
||||
hdr->nm_len = nlh->nlmsg_len;
|
||||
hdr->nm_status = NL_MMAP_STATUS_VALID;
|
||||
|
||||
if (sendto(fd, NULL, 0, 0, &addr, sizeof(addr)) < 0)
|
||||
exit(1);
|
||||
|
||||
/* Advance frame offset to next frame */
|
||||
frame_offset = (frame_offset + frame_size) % ring_size;
|
@ -684,15 +684,343 @@ int main(int argc, char **argp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
+ AF_PACKET TPACKET_V3 example
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
AF_PACKET's TPACKET_V3 ring buffer can be configured to use non-static frame
|
||||
sizes by doing it's own memory management. It is based on blocks where polling
|
||||
works on a per block basis instead of per ring as in TPACKET_V2 and predecessor.
|
||||
|
||||
It is said that TPACKET_V3 brings the following benefits:
|
||||
*) ~15 - 20% reduction in CPU-usage
|
||||
*) ~20% increase in packet capture rate
|
||||
*) ~2x increase in packet density
|
||||
*) Port aggregation analysis
|
||||
*) Non static frame size to capture entire packet payload
|
||||
|
||||
So it seems to be a good candidate to be used with packet fanout.
|
||||
|
||||
Minimal example code by Daniel Borkmann based on Chetan Loke's lolpcap (compile
|
||||
it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.):
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
|
||||
#define BLOCK_SIZE (1 << 22)
|
||||
#define FRAME_SIZE 2048
|
||||
|
||||
#define NUM_BLOCKS 64
|
||||
#define NUM_FRAMES ((BLOCK_SIZE * NUM_BLOCKS) / FRAME_SIZE)
|
||||
|
||||
#define BLOCK_RETIRE_TOV_IN_MS 64
|
||||
#define BLOCK_PRIV_AREA_SZ 13
|
||||
|
||||
#define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1))
|
||||
|
||||
#define BLOCK_STATUS(x) ((x)->h1.block_status)
|
||||
#define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts)
|
||||
#define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt)
|
||||
#define BLOCK_LEN(x) ((x)->h1.blk_len)
|
||||
#define BLOCK_SNUM(x) ((x)->h1.seq_num)
|
||||
#define BLOCK_O2PRIV(x) ((x)->offset_to_priv)
|
||||
#define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x)))
|
||||
#define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc)))
|
||||
#define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri)))
|
||||
|
||||
#ifndef likely
|
||||
# define likely(x) __builtin_expect(!!(x), 1)
|
||||
#endif
|
||||
#ifndef unlikely
|
||||
# define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#endif
|
||||
|
||||
struct block_desc {
|
||||
uint32_t version;
|
||||
uint32_t offset_to_priv;
|
||||
struct tpacket_hdr_v1 h1;
|
||||
};
|
||||
|
||||
struct ring {
|
||||
struct iovec *rd;
|
||||
uint8_t *map;
|
||||
struct tpacket_req3 req;
|
||||
};
|
||||
|
||||
static unsigned long packets_total = 0, bytes_total = 0;
|
||||
static sig_atomic_t sigint = 0;
|
||||
|
||||
void sighandler(int num)
|
||||
{
|
||||
sigint = 1;
|
||||
}
|
||||
|
||||
static int setup_socket(struct ring *ring, char *netdev)
|
||||
{
|
||||
int err, i, fd, v = TPACKET_V3;
|
||||
struct sockaddr_ll ll;
|
||||
|
||||
fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (fd < 0) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
|
||||
if (err < 0) {
|
||||
perror("setsockopt");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&ring->req, 0, sizeof(ring->req));
|
||||
ring->req.tp_block_size = BLOCK_SIZE;
|
||||
ring->req.tp_frame_size = FRAME_SIZE;
|
||||
ring->req.tp_block_nr = NUM_BLOCKS;
|
||||
ring->req.tp_frame_nr = NUM_FRAMES;
|
||||
ring->req.tp_retire_blk_tov = BLOCK_RETIRE_TOV_IN_MS;
|
||||
ring->req.tp_sizeof_priv = BLOCK_PRIV_AREA_SZ;
|
||||
ring->req.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH;
|
||||
|
||||
err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req,
|
||||
sizeof(ring->req));
|
||||
if (err < 0) {
|
||||
perror("setsockopt");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
|
||||
fd, 0);
|
||||
if (ring->map == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ring->rd = malloc(ring->req.tp_block_nr * sizeof(*ring->rd));
|
||||
assert(ring->rd);
|
||||
for (i = 0; i < ring->req.tp_block_nr; ++i) {
|
||||
ring->rd[i].iov_base = ring->map + (i * ring->req.tp_block_size);
|
||||
ring->rd[i].iov_len = ring->req.tp_block_size;
|
||||
}
|
||||
|
||||
memset(&ll, 0, sizeof(ll));
|
||||
ll.sll_family = PF_PACKET;
|
||||
ll.sll_protocol = htons(ETH_P_ALL);
|
||||
ll.sll_ifindex = if_nametoindex(netdev);
|
||||
ll.sll_hatype = 0;
|
||||
ll.sll_pkttype = 0;
|
||||
ll.sll_halen = 0;
|
||||
|
||||
err = bind(fd, (struct sockaddr *) &ll, sizeof(ll));
|
||||
if (err < 0) {
|
||||
perror("bind");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#ifdef __checked
|
||||
static uint64_t prev_block_seq_num = 0;
|
||||
|
||||
void assert_block_seq_num(struct block_desc *pbd)
|
||||
{
|
||||
if (unlikely(prev_block_seq_num + 1 != BLOCK_SNUM(pbd))) {
|
||||
printf("prev_block_seq_num:%"PRIu64", expected seq:%"PRIu64" != "
|
||||
"actual seq:%"PRIu64"\n", prev_block_seq_num,
|
||||
prev_block_seq_num + 1, (uint64_t) BLOCK_SNUM(pbd));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
prev_block_seq_num = BLOCK_SNUM(pbd);
|
||||
}
|
||||
|
||||
static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
|
||||
{
|
||||
if (BLOCK_NUM_PKTS(pbd)) {
|
||||
if (unlikely(bytes != BLOCK_LEN(pbd))) {
|
||||
printf("block:%u with %upackets, expected len:%u != actual len:%u\n",
|
||||
block_num, BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd));
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (unlikely(BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ))) {
|
||||
printf("block:%u, expected len:%lu != actual len:%u\n",
|
||||
block_num, BLOCK_HDR_LEN, BLOCK_LEN(pbd));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void assert_block_header(struct block_desc *pbd, const int block_num)
|
||||
{
|
||||
uint32_t block_status = BLOCK_STATUS(pbd);
|
||||
|
||||
if (unlikely((block_status & TP_STATUS_USER) == 0)) {
|
||||
printf("block:%u, not in TP_STATUS_USER\n", block_num);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
assert_block_seq_num(pbd);
|
||||
}
|
||||
#else
|
||||
static inline void assert_block_header(struct block_desc *pbd, const int block_num)
|
||||
{
|
||||
}
|
||||
static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void display(struct tpacket3_hdr *ppd)
|
||||
{
|
||||
struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac);
|
||||
struct iphdr *ip = (struct iphdr *) ((uint8_t *) eth + ETH_HLEN);
|
||||
|
||||
if (eth->h_proto == htons(ETH_P_IP)) {
|
||||
struct sockaddr_in ss, sd;
|
||||
char sbuff[NI_MAXHOST], dbuff[NI_MAXHOST];
|
||||
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
ss.sin_family = PF_INET;
|
||||
ss.sin_addr.s_addr = ip->saddr;
|
||||
getnameinfo((struct sockaddr *) &ss, sizeof(ss),
|
||||
sbuff, sizeof(sbuff), NULL, 0, NI_NUMERICHOST);
|
||||
|
||||
memset(&sd, 0, sizeof(sd));
|
||||
sd.sin_family = PF_INET;
|
||||
sd.sin_addr.s_addr = ip->daddr;
|
||||
getnameinfo((struct sockaddr *) &sd, sizeof(sd),
|
||||
dbuff, sizeof(dbuff), NULL, 0, NI_NUMERICHOST);
|
||||
|
||||
printf("%s -> %s, ", sbuff, dbuff);
|
||||
}
|
||||
|
||||
printf("rxhash: 0x%x\n", ppd->hv1.tp_rxhash);
|
||||
}
|
||||
|
||||
static void walk_block(struct block_desc *pbd, const int block_num)
|
||||
{
|
||||
int num_pkts = BLOCK_NUM_PKTS(pbd), i;
|
||||
unsigned long bytes = 0;
|
||||
unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ);
|
||||
struct tpacket3_hdr *ppd;
|
||||
|
||||
assert_block_header(pbd, block_num);
|
||||
|
||||
ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd));
|
||||
for (i = 0; i < num_pkts; ++i) {
|
||||
bytes += ppd->tp_snaplen;
|
||||
if (ppd->tp_next_offset)
|
||||
bytes_with_padding += ppd->tp_next_offset;
|
||||
else
|
||||
bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac);
|
||||
|
||||
display(ppd);
|
||||
|
||||
ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset);
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
assert_block_len(pbd, bytes_with_padding, block_num);
|
||||
|
||||
packets_total += num_pkts;
|
||||
bytes_total += bytes;
|
||||
}
|
||||
|
||||
void flush_block(struct block_desc *pbd)
|
||||
{
|
||||
BLOCK_STATUS(pbd) = TP_STATUS_KERNEL;
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
static void teardown_socket(struct ring *ring, int fd)
|
||||
{
|
||||
munmap(ring->map, ring->req.tp_block_size * ring->req.tp_block_nr);
|
||||
free(ring->rd);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int main(int argc, char **argp)
|
||||
{
|
||||
int fd, err;
|
||||
socklen_t len;
|
||||
struct ring ring;
|
||||
struct pollfd pfd;
|
||||
unsigned int block_num = 0;
|
||||
struct block_desc *pbd;
|
||||
struct tpacket_stats_v3 stats;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s INTERFACE\n", argp[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
signal(SIGINT, sighandler);
|
||||
|
||||
memset(&ring, 0, sizeof(ring));
|
||||
fd = setup_socket(&ring, argp[argc - 1]);
|
||||
assert(fd > 0);
|
||||
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
pfd.revents = 0;
|
||||
|
||||
while (likely(!sigint)) {
|
||||
pbd = (struct block_desc *) ring.rd[block_num].iov_base;
|
||||
retry_block:
|
||||
if ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) {
|
||||
poll(&pfd, 1, -1);
|
||||
goto retry_block;
|
||||
}
|
||||
|
||||
walk_block(pbd, block_num);
|
||||
flush_block(pbd);
|
||||
block_num = (block_num + 1) % NUM_BLOCKS;
|
||||
}
|
||||
|
||||
len = sizeof(stats);
|
||||
err = getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len);
|
||||
if (err < 0) {
|
||||
perror("getsockopt");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
printf("\nReceived %u packets, %lu bytes, %u dropped, freeze_q_cnt: %u\n",
|
||||
stats.tp_packets, bytes_total, stats.tp_drops,
|
||||
stats.tp_freeze_q_cnt);
|
||||
|
||||
teardown_socket(&ring, fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
+ PACKET_TIMESTAMP
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
The PACKET_TIMESTAMP setting determines the source of the timestamp in
|
||||
the packet meta information. If your NIC is capable of timestamping
|
||||
packets in hardware, you can request those hardware timestamps to used.
|
||||
Note: you may need to enable the generation of hardware timestamps with
|
||||
SIOCSHWTSTAMP.
|
||||
the packet meta information for mmap(2)ed RX_RING and TX_RINGs. If your
|
||||
NIC is capable of timestamping packets in hardware, you can request those
|
||||
hardware timestamps to be used. Note: you may need to enable the generation
|
||||
of hardware timestamps with SIOCSHWTSTAMP (see related information from
|
||||
Documentation/networking/timestamping.txt).
|
||||
|
||||
PACKET_TIMESTAMP accepts the same integer bit field as
|
||||
SO_TIMESTAMPING. However, only the SOF_TIMESTAMPING_SYS_HARDWARE
|
||||
@ -704,8 +1032,36 @@ SOF_TIMESTAMPING_RAW_HARDWARE if both bits are set.
|
||||
req |= SOF_TIMESTAMPING_SYS_HARDWARE;
|
||||
setsockopt(fd, SOL_PACKET, PACKET_TIMESTAMP, (void *) &req, sizeof(req))
|
||||
|
||||
If PACKET_TIMESTAMP is not set, a software timestamp generated inside
|
||||
the networking stack is used (the behavior before this setting was added).
|
||||
For the mmap(2)ed ring buffers, such timestamps are stored in the
|
||||
tpacket{,2,3}_hdr structure's tp_sec and tp_{n,u}sec members. To determine
|
||||
what kind of timestamp has been reported, the tp_status field is binary |'ed
|
||||
with the following possible bits ...
|
||||
|
||||
TP_STATUS_TS_SYS_HARDWARE
|
||||
TP_STATUS_TS_RAW_HARDWARE
|
||||
TP_STATUS_TS_SOFTWARE
|
||||
|
||||
... that are equivalent to its SOF_TIMESTAMPING_* counterparts. For the
|
||||
RX_RING, if none of those 3 are set (i.e. PACKET_TIMESTAMP is not set),
|
||||
then this means that a software fallback was invoked *within* PF_PACKET's
|
||||
processing code (less precise).
|
||||
|
||||
Getting timestamps for the TX_RING works as follows: i) fill the ring frames,
|
||||
ii) call sendto() e.g. in blocking mode, iii) wait for status of relevant
|
||||
frames to be updated resp. the frame handed over to the application, iv) walk
|
||||
through the frames to pick up the individual hw/sw timestamps.
|
||||
|
||||
Only (!) if transmit timestamping is enabled, then these bits are combined
|
||||
with binary | with TP_STATUS_AVAILABLE, so you must check for that in your
|
||||
application (e.g. !(tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING))
|
||||
in a first step to see if the frame belongs to the application, and then
|
||||
one can extract the type of timestamp in a second step from tp_status)!
|
||||
|
||||
If you don't care about them, thus having it disabled, checking for
|
||||
TP_STATUS_AVAILABLE resp. TP_STATUS_WRONG_FORMAT is sufficient. If in the
|
||||
TX_RING part only TP_STATUS_AVAILABLE is set, then the tp_sec and tp_{n,u}sec
|
||||
members do not contain a valid value. For TX_RINGs, by default no timestamp
|
||||
is generated!
|
||||
|
||||
See include/linux/net_tstamp.h and Documentation/networking/timestamping
|
||||
for more information on hardware timestamps.
|
||||
|
@ -1,6 +1,6 @@
|
||||
STMicroelectronics 10/100/1000 Synopsys Ethernet driver
|
||||
|
||||
Copyright (C) 2007-2010 STMicroelectronics Ltd
|
||||
Copyright (C) 2007-2013 STMicroelectronics Ltd
|
||||
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
|
||||
|
||||
This is the driver for the MAC 10/100/1000 on-chip Ethernet controllers
|
||||
@ -10,7 +10,7 @@ Currently this network device driver is for all STM embedded MAC/GMAC
|
||||
(i.e. 7xxx/5xxx SoCs), SPEAr (arm), Loongson1B (mips) and XLINX XC2V3000
|
||||
FF1152AMT0221 D1215994A VIRTEX FPGA board.
|
||||
|
||||
DWC Ether MAC 10/100/1000 Universal version 3.60a (and older) and DWC Ether
|
||||
DWC Ether MAC 10/100/1000 Universal version 3.70a (and older) and DWC Ether
|
||||
MAC 10/100 Universal version 4.0 have been used for developing this driver.
|
||||
|
||||
This driver supports both the platform bus and PCI.
|
||||
@ -32,6 +32,8 @@ The kernel configuration option is STMMAC_ETH:
|
||||
watchdog: transmit timeout (in milliseconds);
|
||||
flow_ctrl: Flow control ability [on/off];
|
||||
pause: Flow Control Pause Time;
|
||||
eee_timer: tx EEE timer;
|
||||
chain_mode: select chain mode instead of ring.
|
||||
|
||||
3) Command line options
|
||||
Driver parameters can be also passed in command line by using:
|
||||
@ -164,12 +166,12 @@ Where:
|
||||
o bus_setup: perform HW setup of the bus. For example, on some ST platforms
|
||||
this field is used to configure the AMBA bridge to generate more
|
||||
efficient STBus traffic.
|
||||
o init/exit: callbacks used for calling a custom initialisation;
|
||||
o init/exit: callbacks used for calling a custom initialization;
|
||||
this is sometime necessary on some platforms (e.g. ST boxes)
|
||||
where the HW needs to have set some PIO lines or system cfg
|
||||
registers.
|
||||
o custom_cfg/custom_data: this is a custom configuration that can be passed
|
||||
while initialising the resources.
|
||||
while initializing the resources.
|
||||
o bsp_priv: another private poiter.
|
||||
|
||||
For MDIO bus The we have:
|
||||
@ -273,6 +275,8 @@ reset procedure etc).
|
||||
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;
|
||||
o stmmac_hwtstamp.c: HW timestamp support for PTP
|
||||
o stmmac_ptp.c: PTP 1588 clock
|
||||
|
||||
5) Debug Information
|
||||
|
||||
@ -326,6 +330,35 @@ 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:
|
||||
7) Extended descriptors
|
||||
The extended descriptors give us information about the receive Ethernet payload
|
||||
when it is carrying PTP packets or TCP/UDP/ICMP over IP.
|
||||
These are not available on GMAC Synopsys chips older than the 3.50.
|
||||
At probe time the driver will decide if these can be actually used.
|
||||
This support also is mandatory for PTPv2 because the extra descriptors 6 and 7
|
||||
are used for saving the hardware timestamps.
|
||||
|
||||
8) Precision Time Protocol (PTP)
|
||||
The driver supports the IEEE 1588-2002, Precision Time Protocol (PTP),
|
||||
which enables precise synchronization of clocks in measurement and
|
||||
control systems implemented with technologies such as network
|
||||
communication.
|
||||
|
||||
In addition to the basic timestamp features mentioned in IEEE 1588-2002
|
||||
Timestamps, new GMAC cores support the advanced timestamp features.
|
||||
IEEE 1588-2008 that can be enabled when configure the Kernel.
|
||||
|
||||
9) SGMII/RGMII supports
|
||||
New GMAC devices provide own way to manage RGMII/SGMII.
|
||||
This information is available at run-time by looking at the
|
||||
HW capability register. This means that the stmmac can manage
|
||||
auto-negotiation and link status w/o using the PHYLIB stuff
|
||||
In fact, the HW provides a subset of extended registers to
|
||||
restart the ANE, verify Full/Half duplex mode and Speed.
|
||||
Also thanks to these registers it is possible to look at the
|
||||
Auto-negotiated Link Parter Ability.
|
||||
|
||||
10) TODO:
|
||||
o XGMAC is not supported.
|
||||
o Add the PTP - precision time protocol
|
||||
o Complete the TBI & RTBI support.
|
||||
o extened VLAN support for 3.70a SYNP GMAC.
|
||||
|
@ -1767,7 +1767,7 @@ F: arch/arm/configs/bcm2835_defconfig
|
||||
F: drivers/*/*bcm2835*
|
||||
|
||||
BROADCOM TG3 GIGABIT ETHERNET DRIVER
|
||||
M: Matt Carlson <mcarlson@broadcom.com>
|
||||
M: Nithin Nayak Sujir <nsujir@broadcom.com>
|
||||
M: Michael Chan <mchan@broadcom.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
@ -1889,7 +1889,7 @@ F: Documentation/video4linux/cafe_ccic
|
||||
F: drivers/media/platform/marvell-ccic/
|
||||
|
||||
CAIF NETWORK LAYER
|
||||
M: Sjur Braendeland <sjur.brandeland@stericsson.com>
|
||||
M: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/networking/caif/
|
||||
@ -6396,6 +6396,7 @@ F: drivers/acpi/apei/erst.c
|
||||
|
||||
PTP HARDWARE CLOCK SUPPORT
|
||||
M: Richard Cochran <richardcochran@gmail.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
W: http://linuxptp.sourceforge.net/
|
||||
F: Documentation/ABI/testing/sysfs-ptp
|
||||
@ -6527,6 +6528,7 @@ S: Supported
|
||||
F: drivers/net/ethernet/qlogic/qlcnic/
|
||||
|
||||
QLOGIC QLGE 10Gb ETHERNET DRIVER
|
||||
M: Shahed Shaikh <shahed.shaikh@qlogic.com>
|
||||
M: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
|
||||
M: Ron Mercer <ron.mercer@qlogic.com>
|
||||
M: linux-driver@qlogic.com
|
||||
@ -8627,7 +8629,7 @@ F: drivers/usb/gadget/*uvc*.c
|
||||
F: drivers/usb/gadget/webcam.c
|
||||
|
||||
USB WIRELESS RNDIS DRIVER (rndis_wlan)
|
||||
M: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
|
||||
M: Jussi Kivilinna <jussi.kivilinna@iki.fi>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/rndis_wlan.c
|
||||
|
@ -79,4 +79,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _UAPI_ASM_SOCKET_H */
|
||||
|
@ -349,7 +349,7 @@ mac: ethernet@4a100000 {
|
||||
rx_descs = <64>;
|
||||
mac_control = <0x20>;
|
||||
slaves = <2>;
|
||||
cpts_active_slave = <0>;
|
||||
active_slave = <0>;
|
||||
cpts_clock_mult = <0x80000000>;
|
||||
cpts_clock_shift = <29>;
|
||||
reg = <0x4a100000 0x800
|
||||
|
@ -918,9 +918,8 @@ void bpf_jit_compile(struct sk_filter *fp)
|
||||
#endif
|
||||
|
||||
if (bpf_jit_enable > 1)
|
||||
print_hex_dump(KERN_INFO, "BPF JIT code: ",
|
||||
DUMP_PREFIX_ADDRESS, 16, 4, ctx.target,
|
||||
alloc_size, false);
|
||||
/* there are 2 passes here */
|
||||
bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
|
||||
|
||||
fp->bpf_func = (void *)ctx.target;
|
||||
out:
|
||||
|
@ -238,6 +238,7 @@ static __init void ge_complete(
|
||||
struct mv643xx_eth_shared_platform_data *orion_ge_shared_data,
|
||||
struct resource *orion_ge_resource, unsigned long irq,
|
||||
struct platform_device *orion_ge_shared,
|
||||
struct platform_device *orion_ge_mvmdio,
|
||||
struct mv643xx_eth_platform_data *eth_data,
|
||||
struct platform_device *orion_ge)
|
||||
{
|
||||
@ -247,6 +248,8 @@ static __init void ge_complete(
|
||||
orion_ge->dev.platform_data = eth_data;
|
||||
|
||||
platform_device_register(orion_ge_shared);
|
||||
if (orion_ge_mvmdio)
|
||||
platform_device_register(orion_ge_mvmdio);
|
||||
platform_device_register(orion_ge);
|
||||
}
|
||||
|
||||
@ -258,8 +261,6 @@ struct mv643xx_eth_shared_platform_data orion_ge00_shared_data;
|
||||
static struct resource orion_ge00_shared_resources[] = {
|
||||
{
|
||||
.name = "ge00 base",
|
||||
}, {
|
||||
.name = "ge00 err irq",
|
||||
},
|
||||
};
|
||||
|
||||
@ -271,6 +272,19 @@ static struct platform_device orion_ge00_shared = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource orion_ge_mvmdio_resources[] = {
|
||||
{
|
||||
.name = "ge00 mvmdio base",
|
||||
}, {
|
||||
.name = "ge00 mvmdio err irq",
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device orion_ge_mvmdio = {
|
||||
.name = "orion-mdio",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static struct resource orion_ge00_resources[] = {
|
||||
{
|
||||
.name = "ge00 irq",
|
||||
@ -295,26 +309,25 @@ void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned int tx_csum_limit)
|
||||
{
|
||||
fill_resources(&orion_ge00_shared, orion_ge00_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
fill_resources(&orion_ge_mvmdio, orion_ge_mvmdio_resources,
|
||||
mapbase + 0x2004, 0x84 - 1, irq_err);
|
||||
orion_ge00_shared_data.tx_csum_limit = tx_csum_limit;
|
||||
ge_complete(&orion_ge00_shared_data,
|
||||
orion_ge00_resources, irq, &orion_ge00_shared,
|
||||
&orion_ge_mvmdio,
|
||||
eth_data, &orion_ge00);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* GE01
|
||||
****************************************************************************/
|
||||
struct mv643xx_eth_shared_platform_data orion_ge01_shared_data = {
|
||||
.shared_smi = &orion_ge00_shared,
|
||||
};
|
||||
struct mv643xx_eth_shared_platform_data orion_ge01_shared_data;
|
||||
|
||||
static struct resource orion_ge01_shared_resources[] = {
|
||||
{
|
||||
.name = "ge01 base",
|
||||
}, {
|
||||
.name = "ge01 err irq",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device orion_ge01_shared = {
|
||||
@ -349,26 +362,23 @@ void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned int tx_csum_limit)
|
||||
{
|
||||
fill_resources(&orion_ge01_shared, orion_ge01_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
orion_ge01_shared_data.tx_csum_limit = tx_csum_limit;
|
||||
ge_complete(&orion_ge01_shared_data,
|
||||
orion_ge01_resources, irq, &orion_ge01_shared,
|
||||
NULL,
|
||||
eth_data, &orion_ge01);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* GE10
|
||||
****************************************************************************/
|
||||
struct mv643xx_eth_shared_platform_data orion_ge10_shared_data = {
|
||||
.shared_smi = &orion_ge00_shared,
|
||||
};
|
||||
struct mv643xx_eth_shared_platform_data orion_ge10_shared_data;
|
||||
|
||||
static struct resource orion_ge10_shared_resources[] = {
|
||||
{
|
||||
.name = "ge10 base",
|
||||
}, {
|
||||
.name = "ge10 err irq",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device orion_ge10_shared = {
|
||||
@ -402,24 +412,21 @@ void __init orion_ge10_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned long irq_err)
|
||||
{
|
||||
fill_resources(&orion_ge10_shared, orion_ge10_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
ge_complete(&orion_ge10_shared_data,
|
||||
orion_ge10_resources, irq, &orion_ge10_shared,
|
||||
NULL,
|
||||
eth_data, &orion_ge10);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* GE11
|
||||
****************************************************************************/
|
||||
struct mv643xx_eth_shared_platform_data orion_ge11_shared_data = {
|
||||
.shared_smi = &orion_ge00_shared,
|
||||
};
|
||||
struct mv643xx_eth_shared_platform_data orion_ge11_shared_data;
|
||||
|
||||
static struct resource orion_ge11_shared_resources[] = {
|
||||
{
|
||||
.name = "ge11 base",
|
||||
}, {
|
||||
.name = "ge11 err irq",
|
||||
},
|
||||
};
|
||||
|
||||
@ -454,9 +461,10 @@ void __init orion_ge11_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned long irq_err)
|
||||
{
|
||||
fill_resources(&orion_ge11_shared, orion_ge11_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
ge_complete(&orion_ge11_shared_data,
|
||||
orion_ge11_resources, irq, &orion_ge11_shared,
|
||||
NULL,
|
||||
eth_data, &orion_ge11);
|
||||
}
|
||||
|
||||
|
@ -72,4 +72,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* __ASM_AVR32_SOCKET_H */
|
||||
|
@ -74,6 +74,8 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
||||
|
||||
|
@ -72,5 +72,7 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
||||
|
@ -72,4 +72,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
@ -81,4 +81,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_IA64_SOCKET_H */
|
||||
|
@ -72,4 +72,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_M32R_SOCKET_H */
|
||||
|
@ -90,4 +90,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _UAPI_ASM_SOCKET_H */
|
||||
|
@ -72,4 +72,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
@ -71,6 +71,8 @@
|
||||
|
||||
#define SO_LOCK_FILTER 0x4025
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 0x4026
|
||||
|
||||
/* O_NONBLOCK clashes with the bits used for socket types. Therefore we
|
||||
* have to define SOCK_NONBLOCK to a different value here.
|
||||
*/
|
||||
|
@ -79,4 +79,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_POWERPC_SOCKET_H */
|
||||
|
@ -671,16 +671,12 @@ void bpf_jit_compile(struct sk_filter *fp)
|
||||
}
|
||||
|
||||
if (bpf_jit_enable > 1)
|
||||
pr_info("flen=%d proglen=%u pass=%d image=%p\n",
|
||||
flen, proglen, pass, image);
|
||||
/* Note that we output the base address of the code_base
|
||||
* rather than image, since opcodes are in code_base.
|
||||
*/
|
||||
bpf_jit_dump(flen, proglen, pass, code_base);
|
||||
|
||||
if (image) {
|
||||
if (bpf_jit_enable > 1)
|
||||
print_hex_dump(KERN_ERR, "JIT code: ",
|
||||
DUMP_PREFIX_ADDRESS,
|
||||
16, 1, code_base,
|
||||
proglen, false);
|
||||
|
||||
bpf_flush_icache(code_base, code_base + (proglen/4));
|
||||
/* Function descriptor nastiness: Address + TOC */
|
||||
((u64 *)image)[0] = (u64)code_base;
|
||||
|
@ -47,6 +47,25 @@ static struct platform_device mv643xx_eth_shared_device = {
|
||||
.resource = mv643xx_eth_shared_resources,
|
||||
};
|
||||
|
||||
/*
|
||||
* The orion mdio driver only covers shared + 0x4 up to shared + 0x84 - 1
|
||||
*/
|
||||
static struct resource mv643xx_eth_mvmdio_resources[] = {
|
||||
[0] = {
|
||||
.name = "ethernet mdio base",
|
||||
.start = 0xf1000000 + MV643XX_ETH_SHARED_REGS + 0x4,
|
||||
.end = 0xf1000000 + MV643XX_ETH_SHARED_REGS + 0x83,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device mv643xx_eth_mvmdio_device = {
|
||||
.name = "orion-mdio",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(mv643xx_eth_mvmdio_resources),
|
||||
.resource = mv643xx_eth_shared_resources,
|
||||
};
|
||||
|
||||
static struct resource mv643xx_eth_port1_resources[] = {
|
||||
[0] = {
|
||||
.name = "eth port1 irq",
|
||||
@ -82,6 +101,7 @@ static struct platform_device eth_port1_device = {
|
||||
|
||||
static struct platform_device *mv643xx_eth_pd_devs[] __initdata = {
|
||||
&mv643xx_eth_shared_device,
|
||||
&mv643xx_eth_mvmdio_device,
|
||||
ð_port1_device,
|
||||
};
|
||||
|
||||
|
@ -214,15 +214,27 @@ static struct platform_device * __init mv64x60_eth_register_shared_pdev(
|
||||
struct device_node *np, int id)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource r[1];
|
||||
struct resource r[2];
|
||||
int err;
|
||||
|
||||
err = of_address_to_resource(np, 0, &r[0]);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
/* register an orion mdio bus driver */
|
||||
r[1].start = r[0].start + 0x4;
|
||||
r[1].end = r[0].start + 0x84 - 1;
|
||||
r[1].flags = IORESOURCE_MEM;
|
||||
|
||||
if (id == 0) {
|
||||
pdev = platform_device_register_simple("orion-mdio", -1, &r[1], 1);
|
||||
if (!pdev)
|
||||
return pdev;
|
||||
}
|
||||
|
||||
pdev = platform_device_register_simple(MV643XX_ETH_SHARED_NAME, id,
|
||||
r, 1);
|
||||
&r[0], 1);
|
||||
|
||||
return pdev;
|
||||
}
|
||||
|
||||
|
@ -78,4 +78,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
@ -68,6 +68,8 @@
|
||||
|
||||
#define SO_LOCK_FILTER 0x0028
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 0x0029
|
||||
|
||||
/* Security levels - as per NRL IPv6 - don't actually do anything */
|
||||
#define SO_SECURITY_AUTHENTICATION 0x5001
|
||||
#define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
|
||||
|
@ -795,13 +795,9 @@ cond_branch: f_offset = addrs[i + filter[i].jf];
|
||||
}
|
||||
|
||||
if (bpf_jit_enable > 1)
|
||||
pr_err("flen=%d proglen=%u pass=%d image=%p\n",
|
||||
flen, proglen, pass, image);
|
||||
bpf_jit_dump(flen, proglen, pass, image);
|
||||
|
||||
if (image) {
|
||||
if (bpf_jit_enable > 1)
|
||||
print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS,
|
||||
16, 1, image, proglen, false);
|
||||
bpf_flush_icache(image, image + proglen);
|
||||
fp->bpf_func = (void *)image;
|
||||
}
|
||||
|
@ -725,17 +725,12 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
|
||||
}
|
||||
oldproglen = proglen;
|
||||
}
|
||||
|
||||
if (bpf_jit_enable > 1)
|
||||
pr_err("flen=%d proglen=%u pass=%d image=%p\n",
|
||||
flen, proglen, pass, image);
|
||||
bpf_jit_dump(flen, proglen, pass, image);
|
||||
|
||||
if (image) {
|
||||
if (bpf_jit_enable > 1)
|
||||
print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS,
|
||||
16, 1, image, proglen, false);
|
||||
|
||||
bpf_flush_icache(image, image + proglen);
|
||||
|
||||
fp->bpf_func = (void *)image;
|
||||
}
|
||||
out:
|
||||
|
@ -83,4 +83,6 @@
|
||||
|
||||
#define SO_LOCK_FILTER 44
|
||||
|
||||
#define SO_SELECT_ERR_QUEUE 45
|
||||
|
||||
#endif /* _XTENSA_SOCKET_H */
|
||||
|
@ -1055,7 +1055,7 @@ static int he_start(struct atm_dev *dev)
|
||||
he_writel(he_dev, 0x0, RESET_CNTL);
|
||||
he_writel(he_dev, 0xff, RESET_CNTL);
|
||||
|
||||
udelay(16*1000); /* 16 ms */
|
||||
msleep(16); /* 16 ms */
|
||||
status = he_readl(he_dev, RESET_CNTL);
|
||||
if ((status & BOARD_RST_STATUS) == 0) {
|
||||
hprintk("reset failed\n");
|
||||
|
@ -104,7 +104,13 @@ void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
|
||||
if (i)
|
||||
bcma_err(core->bus, "PLL enable timeout\n");
|
||||
} else {
|
||||
bcma_warn(core->bus, "Disabling PLL not supported yet!\n");
|
||||
/*
|
||||
* Mask the PLL but don't wait for it to be disabled. PLL may be
|
||||
* shared between cores and will be still up if there is another
|
||||
* core using it.
|
||||
*/
|
||||
bcma_mask32(core, BCMA_CLKCTLST, ~req);
|
||||
bcma_read32(core, BCMA_CLKCTLST);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_core_pll_ctl);
|
||||
|
@ -25,13 +25,14 @@ static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc)
|
||||
u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc)
|
||||
{
|
||||
if (cc->capabilities & BCMA_CC_CAP_PMU)
|
||||
return bcma_pmu_get_alp_clock(cc);
|
||||
|
||||
return 20000000;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_get_alp_clock);
|
||||
|
||||
static u32 bcma_chipco_watchdog_get_max_timer(struct bcma_drv_cc *cc)
|
||||
{
|
||||
@ -213,6 +214,7 @@ u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value)
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_gpio_out);
|
||||
|
||||
u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
|
||||
{
|
||||
@ -225,6 +227,7 @@ u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_gpio_outen);
|
||||
|
||||
/*
|
||||
* If the bit is set to 0, chipcommon controlls this GPIO,
|
||||
|
@ -174,19 +174,35 @@ u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc)
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
switch (bus->chipinfo.id) {
|
||||
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_BCM43224:
|
||||
case BCMA_CHIP_ID_BCM43225:
|
||||
case BCMA_CHIP_ID_BCM43227:
|
||||
case BCMA_CHIP_ID_BCM43228:
|
||||
case BCMA_CHIP_ID_BCM4331:
|
||||
case BCMA_CHIP_ID_BCM43421:
|
||||
case BCMA_CHIP_ID_BCM43428:
|
||||
case BCMA_CHIP_ID_BCM43431:
|
||||
case BCMA_CHIP_ID_BCM4716:
|
||||
case BCMA_CHIP_ID_BCM47162:
|
||||
case BCMA_CHIP_ID_BCM4748:
|
||||
case BCMA_CHIP_ID_BCM4749:
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM53572:
|
||||
case BCMA_CHIP_ID_BCM6362:
|
||||
/* always 20Mhz */
|
||||
return 20000 * 1000;
|
||||
case BCMA_CHIP_ID_BCM5356:
|
||||
case BCMA_CHIP_ID_BCM4706:
|
||||
case BCMA_CHIP_ID_BCM5356:
|
||||
/* always 25Mhz */
|
||||
return 25000 * 1000;
|
||||
case BCMA_CHIP_ID_BCM43460:
|
||||
case BCMA_CHIP_ID_BCM4352:
|
||||
case BCMA_CHIP_ID_BCM4360:
|
||||
if (cc->status & BCMA_CC_CHIPST_4360_XTAL_40MZ)
|
||||
return 40000 * 1000;
|
||||
else
|
||||
return 20000 * 1000;
|
||||
default:
|
||||
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);
|
||||
@ -373,7 +389,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
||||
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;
|
||||
tmp = BCMA_CC_PMU_CTL_PLL_UPD;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM4331:
|
||||
@ -394,7 +410,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
|
||||
0x03000a08);
|
||||
}
|
||||
tmp = 1 << 10;
|
||||
tmp = BCMA_CC_PMU_CTL_PLL_UPD;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM43224:
|
||||
@ -427,7 +443,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
}
|
||||
tmp = 1 << 10;
|
||||
tmp = BCMA_CC_PMU_CTL_PLL_UPD;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM4716:
|
||||
@ -461,7 +477,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
||||
0x88888815);
|
||||
}
|
||||
|
||||
tmp = 3 << 9;
|
||||
tmp = BCMA_CC_PMU_CTL_PLL_UPD | BCMA_CC_PMU_CTL_NOILPONW;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM43227:
|
||||
@ -497,7 +513,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
|
||||
0x88888815);
|
||||
}
|
||||
tmp = 1 << 10;
|
||||
tmp = BCMA_CC_PMU_CTL_PLL_UPD;
|
||||
break;
|
||||
default:
|
||||
bcma_err(bus, "Unknown spuravoidance settings for chip 0x%04X, not changing PLL\n",
|
||||
|
@ -120,6 +120,11 @@ static int bcma_register_cores(struct bcma_bus *bus)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only first GMAC core on BCM4706 is connected and working */
|
||||
if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
|
||||
core->core_unit > 0)
|
||||
continue;
|
||||
|
||||
core->dev.release = bcma_release_core_dev;
|
||||
core->dev.bus = &bcma_bus_type;
|
||||
dev_set_name(&core->dev, "bcma%d:%d", bus->num, dev_id);
|
||||
|
@ -137,19 +137,19 @@ static void bcma_scan_switch_core(struct bcma_bus *bus, u32 addr)
|
||||
addr);
|
||||
}
|
||||
|
||||
static u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 **eromptr)
|
||||
static u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 __iomem **eromptr)
|
||||
{
|
||||
u32 ent = readl(*eromptr);
|
||||
(*eromptr)++;
|
||||
return ent;
|
||||
}
|
||||
|
||||
static void bcma_erom_push_ent(u32 **eromptr)
|
||||
static void bcma_erom_push_ent(u32 __iomem **eromptr)
|
||||
{
|
||||
(*eromptr)--;
|
||||
}
|
||||
|
||||
static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 **eromptr)
|
||||
static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 __iomem **eromptr)
|
||||
{
|
||||
u32 ent = bcma_erom_get_ent(bus, eromptr);
|
||||
if (!(ent & SCAN_ER_VALID))
|
||||
@ -159,14 +159,14 @@ static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 **eromptr)
|
||||
return ent;
|
||||
}
|
||||
|
||||
static bool bcma_erom_is_end(struct bcma_bus *bus, u32 **eromptr)
|
||||
static bool bcma_erom_is_end(struct bcma_bus *bus, u32 __iomem **eromptr)
|
||||
{
|
||||
u32 ent = bcma_erom_get_ent(bus, eromptr);
|
||||
bcma_erom_push_ent(eromptr);
|
||||
return (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID));
|
||||
}
|
||||
|
||||
static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 **eromptr)
|
||||
static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 __iomem **eromptr)
|
||||
{
|
||||
u32 ent = bcma_erom_get_ent(bus, eromptr);
|
||||
bcma_erom_push_ent(eromptr);
|
||||
@ -175,7 +175,7 @@ static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 **eromptr)
|
||||
((ent & SCAN_ADDR_TYPE) == SCAN_ADDR_TYPE_BRIDGE));
|
||||
}
|
||||
|
||||
static void bcma_erom_skip_component(struct bcma_bus *bus, u32 **eromptr)
|
||||
static void bcma_erom_skip_component(struct bcma_bus *bus, u32 __iomem **eromptr)
|
||||
{
|
||||
u32 ent;
|
||||
while (1) {
|
||||
@ -189,7 +189,7 @@ static void bcma_erom_skip_component(struct bcma_bus *bus, u32 **eromptr)
|
||||
bcma_erom_push_ent(eromptr);
|
||||
}
|
||||
|
||||
static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 **eromptr)
|
||||
static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 __iomem **eromptr)
|
||||
{
|
||||
u32 ent = bcma_erom_get_ent(bus, eromptr);
|
||||
if (!(ent & SCAN_ER_VALID))
|
||||
@ -199,7 +199,7 @@ static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 **eromptr)
|
||||
return ent;
|
||||
}
|
||||
|
||||
static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 **eromptr,
|
||||
static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
|
||||
u32 type, u8 port)
|
||||
{
|
||||
u32 addrl, addrh, sizel, sizeh = 0;
|
||||
|
@ -217,6 +217,7 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
|
||||
}
|
||||
|
||||
SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
|
||||
SPEX(board_type, SSB_SPROM1_SPID, ~0, 0);
|
||||
|
||||
SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
|
||||
SSB_SPROM4_TXPID2G0_SHIFT);
|
||||
|
@ -90,6 +90,7 @@ static struct usb_device_id ath3k_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3393) },
|
||||
{ USB_DEVICE(0x0489, 0xe04e) },
|
||||
{ USB_DEVICE(0x0489, 0xe056) },
|
||||
{ USB_DEVICE(0x0489, 0xe04d) },
|
||||
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xE02C) },
|
||||
@ -126,6 +127,7 @@ static struct usb_device_id ath3k_blist_tbl[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* Atheros AR5BBU22 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
|
||||
|
@ -29,20 +29,6 @@
|
||||
struct btmrvl_debugfs_data {
|
||||
struct dentry *config_dir;
|
||||
struct dentry *status_dir;
|
||||
|
||||
/* config */
|
||||
struct dentry *psmode;
|
||||
struct dentry *pscmd;
|
||||
struct dentry *hsmode;
|
||||
struct dentry *hscmd;
|
||||
struct dentry *gpiogap;
|
||||
struct dentry *hscfgcmd;
|
||||
|
||||
/* status */
|
||||
struct dentry *curpsmode;
|
||||
struct dentry *hsstate;
|
||||
struct dentry *psstate;
|
||||
struct dentry *txdnldready;
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_hscfgcmd_write(struct file *file,
|
||||
@ -91,47 +77,6 @@ static const struct file_operations btmrvl_hscfgcmd_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_psmode_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
long result, ret;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = strict_strtol(buf, 10, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->btmrvl_dev.psmode = result;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t btmrvl_psmode_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
|
||||
priv->btmrvl_dev.psmode);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations btmrvl_psmode_fops = {
|
||||
.read = btmrvl_psmode_read,
|
||||
.write = btmrvl_psmode_write,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -178,47 +123,6 @@ static const struct file_operations btmrvl_pscmd_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_gpiogap_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
long result, ret;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = strict_strtol(buf, 16, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->btmrvl_dev.gpio_gap = result;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t btmrvl_gpiogap_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf) - 1, "0x%x\n",
|
||||
priv->btmrvl_dev.gpio_gap);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations btmrvl_gpiogap_fops = {
|
||||
.read = btmrvl_gpiogap_read,
|
||||
.write = btmrvl_gpiogap_write,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -263,119 +167,6 @@ static const struct file_operations btmrvl_hscmd_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_hsmode_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
long result, ret;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = strict_strtol(buf, 10, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->btmrvl_dev.hsmode = result;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t btmrvl_hsmode_read(struct file *file, char __user * userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hsmode);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations btmrvl_hsmode_fops = {
|
||||
.read = btmrvl_hsmode_read,
|
||||
.write = btmrvl_hsmode_write,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_curpsmode_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->psmode);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations btmrvl_curpsmode_fops = {
|
||||
.read = btmrvl_curpsmode_read,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_psstate_read(struct file *file, char __user * userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->ps_state);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations btmrvl_psstate_fops = {
|
||||
.read = btmrvl_psstate_read,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_hsstate_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->hs_state);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations btmrvl_hsstate_fops = {
|
||||
.read = btmrvl_hsstate_read,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t btmrvl_txdnldready_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btmrvl_private *priv = file->private_data;
|
||||
char buf[16];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
|
||||
priv->btmrvl_dev.tx_dnld_rdy);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations btmrvl_txdnldready_fops = {
|
||||
.read = btmrvl_txdnldready_read,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
void btmrvl_debugfs_init(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmrvl_private *priv = hci_get_drvdata(hdev);
|
||||
@ -394,30 +185,28 @@ void btmrvl_debugfs_init(struct hci_dev *hdev)
|
||||
|
||||
dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
|
||||
|
||||
dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_psmode_fops);
|
||||
dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_pscmd_fops);
|
||||
dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_gpiogap_fops);
|
||||
dbg->hsmode = debugfs_create_file("hsmode", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_hsmode_fops);
|
||||
dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_hscmd_fops);
|
||||
dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_hscfgcmd_fops);
|
||||
debugfs_create_u8("psmode", 0644, dbg->config_dir,
|
||||
&priv->btmrvl_dev.psmode);
|
||||
debugfs_create_file("pscmd", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_pscmd_fops);
|
||||
debugfs_create_x16("gpiogap", 0644, dbg->config_dir,
|
||||
&priv->btmrvl_dev.gpio_gap);
|
||||
debugfs_create_u8("hsmode", 0644, dbg->config_dir,
|
||||
&priv->btmrvl_dev.hsmode);
|
||||
debugfs_create_file("hscmd", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_hscmd_fops);
|
||||
debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
|
||||
priv, &btmrvl_hscfgcmd_fops);
|
||||
|
||||
dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
|
||||
dbg->curpsmode = debugfs_create_file("curpsmode", 0444,
|
||||
dbg->status_dir, priv,
|
||||
&btmrvl_curpsmode_fops);
|
||||
dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir,
|
||||
priv, &btmrvl_psstate_fops);
|
||||
dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir,
|
||||
priv, &btmrvl_hsstate_fops);
|
||||
dbg->txdnldready = debugfs_create_file("txdnldready", 0444,
|
||||
dbg->status_dir, priv,
|
||||
&btmrvl_txdnldready_fops);
|
||||
debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
|
||||
&priv->adapter->psmode);
|
||||
debugfs_create_u8("psstate", 0444, dbg->status_dir,
|
||||
&priv->adapter->ps_state);
|
||||
debugfs_create_u8("hsstate", 0444, dbg->status_dir,
|
||||
&priv->adapter->hs_state);
|
||||
debugfs_create_u8("txdnldready", 0444, dbg->status_dir,
|
||||
&priv->btmrvl_dev.tx_dnld_rdy);
|
||||
}
|
||||
|
||||
void btmrvl_debugfs_remove(struct hci_dev *hdev)
|
||||
@ -428,19 +217,8 @@ void btmrvl_debugfs_remove(struct hci_dev *hdev)
|
||||
if (!dbg)
|
||||
return;
|
||||
|
||||
debugfs_remove(dbg->psmode);
|
||||
debugfs_remove(dbg->pscmd);
|
||||
debugfs_remove(dbg->gpiogap);
|
||||
debugfs_remove(dbg->hsmode);
|
||||
debugfs_remove(dbg->hscmd);
|
||||
debugfs_remove(dbg->hscfgcmd);
|
||||
debugfs_remove(dbg->config_dir);
|
||||
|
||||
debugfs_remove(dbg->curpsmode);
|
||||
debugfs_remove(dbg->psstate);
|
||||
debugfs_remove(dbg->hsstate);
|
||||
debugfs_remove(dbg->txdnldready);
|
||||
debugfs_remove(dbg->status_dir);
|
||||
debugfs_remove_recursive(dbg->config_dir);
|
||||
debugfs_remove_recursive(dbg->status_dir);
|
||||
|
||||
kfree(dbg);
|
||||
}
|
||||
|
@ -83,8 +83,8 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
|
||||
.helper = "sd8688_helper.bin",
|
||||
.firmware = "sd8688.bin",
|
||||
.helper = "mrvl/sd8688_helper.bin",
|
||||
.firmware = "mrvl/sd8688.bin",
|
||||
.reg = &btmrvl_reg_8688,
|
||||
.sd_blksz_fw_dl = 64,
|
||||
};
|
||||
@ -228,24 +228,24 @@ static int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits)
|
||||
static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card,
|
||||
int pollnum)
|
||||
{
|
||||
int ret = -ETIMEDOUT;
|
||||
u16 firmwarestat;
|
||||
unsigned int tries;
|
||||
int tries, ret;
|
||||
|
||||
/* Wait for firmware to become ready */
|
||||
for (tries = 0; tries < pollnum; tries++) {
|
||||
if (btmrvl_sdio_read_fw_status(card, &firmwarestat) < 0)
|
||||
sdio_claim_host(card->func);
|
||||
ret = btmrvl_sdio_read_fw_status(card, &firmwarestat);
|
||||
sdio_release_host(card->func);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
if (firmwarestat == FIRMWARE_READY) {
|
||||
ret = 0;
|
||||
break;
|
||||
} else {
|
||||
msleep(10);
|
||||
}
|
||||
if (firmwarestat == FIRMWARE_READY)
|
||||
return 0;
|
||||
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card)
|
||||
@ -874,7 +874,7 @@ static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
|
||||
|
||||
static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
u8 fws0;
|
||||
int pollnum = MAX_POLL_TRIES;
|
||||
|
||||
@ -882,13 +882,14 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
|
||||
BT_ERR("card or function is NULL!");
|
||||
return -EINVAL;
|
||||
}
|
||||
sdio_claim_host(card->func);
|
||||
|
||||
if (!btmrvl_sdio_verify_fw_download(card, 1)) {
|
||||
BT_DBG("Firmware already downloaded!");
|
||||
goto done;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sdio_claim_host(card->func);
|
||||
|
||||
/* Check if other function driver is downloading the firmware */
|
||||
fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret);
|
||||
if (ret) {
|
||||
@ -918,15 +919,21 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
|
||||
}
|
||||
}
|
||||
|
||||
sdio_release_host(card->func);
|
||||
|
||||
/*
|
||||
* winner or not, with this test the FW synchronizes when the
|
||||
* module can continue its initialization
|
||||
*/
|
||||
if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
|
||||
BT_ERR("FW failed to be active in time!");
|
||||
ret = -ETIMEDOUT;
|
||||
goto done;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
done:
|
||||
sdio_release_host(card->func);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -989,8 +996,6 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
|
||||
goto unreg_dev;
|
||||
}
|
||||
|
||||
msleep(100);
|
||||
|
||||
btmrvl_sdio_enable_host_int(card);
|
||||
|
||||
priv = btmrvl_add_card(card);
|
||||
@ -1185,7 +1190,7 @@ MODULE_AUTHOR("Marvell International Ltd.");
|
||||
MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_FIRMWARE("sd8688_helper.bin");
|
||||
MODULE_FIRMWARE("sd8688.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8688_helper.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8688.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@ -47,6 +48,7 @@ static struct usb_driver btusb_driver;
|
||||
#define BTUSB_BROKEN_ISOC 0x20
|
||||
#define BTUSB_WRONG_SCO_MTU 0x40
|
||||
#define BTUSB_ATH3012 0x80
|
||||
#define BTUSB_INTEL 0x100
|
||||
|
||||
static struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
@ -148,6 +150,7 @@ static struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
|
||||
@ -206,6 +209,9 @@ static struct usb_device_id blacklist_table[] = {
|
||||
/* Frontline ComProbe Bluetooth Sniffer */
|
||||
{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
|
||||
|
||||
/* Intel Bluetooth device */
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
@ -926,6 +932,391 @@ static void btusb_waker(struct work_struct *work)
|
||||
usb_autopm_put_interface(data->intf);
|
||||
}
|
||||
|
||||
static int btusb_setup_bcm92035(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 val = 0x00;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc3b, 1, &val, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb))
|
||||
BT_ERR("BCM92035 command failed (%ld)", -PTR_ERR(skb));
|
||||
else
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct intel_version {
|
||||
u8 status;
|
||||
u8 hw_platform;
|
||||
u8 hw_variant;
|
||||
u8 hw_revision;
|
||||
u8 fw_variant;
|
||||
u8 fw_revision;
|
||||
u8 fw_build_num;
|
||||
u8 fw_build_ww;
|
||||
u8 fw_build_yy;
|
||||
u8 fw_patch_num;
|
||||
} __packed;
|
||||
|
||||
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
||||
struct intel_version *ver)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
char fwname[64];
|
||||
int ret;
|
||||
|
||||
snprintf(fwname, sizeof(fwname),
|
||||
"intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.bseq",
|
||||
ver->hw_platform, ver->hw_variant, ver->hw_revision,
|
||||
ver->fw_variant, ver->fw_revision, ver->fw_build_num,
|
||||
ver->fw_build_ww, ver->fw_build_yy);
|
||||
|
||||
ret = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (ret < 0) {
|
||||
if (ret == -EINVAL) {
|
||||
BT_ERR("%s Intel firmware file request failed (%d)",
|
||||
hdev->name, ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BT_ERR("%s failed to open Intel firmware file: %s(%d)",
|
||||
hdev->name, fwname, ret);
|
||||
|
||||
/* If the correct firmware patch file is not found, use the
|
||||
* default firmware patch file instead
|
||||
*/
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-hw-%x.%x.bseq",
|
||||
ver->hw_platform, ver->hw_variant);
|
||||
if (request_firmware(&fw, fwname, &hdev->dev) < 0) {
|
||||
BT_ERR("%s failed to open default Intel fw file: %s",
|
||||
hdev->name, fwname);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BT_INFO("%s: Intel Bluetooth firmware file: %s", hdev->name, fwname);
|
||||
|
||||
return fw;
|
||||
}
|
||||
|
||||
static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
||||
const struct firmware *fw,
|
||||
const u8 **fw_ptr, int *disable_patch)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct hci_command_hdr *cmd;
|
||||
const u8 *cmd_param;
|
||||
struct hci_event_hdr *evt = NULL;
|
||||
const u8 *evt_param = NULL;
|
||||
int remain = fw->size - (*fw_ptr - fw->data);
|
||||
|
||||
/* The first byte indicates the types of the patch command or event.
|
||||
* 0x01 means HCI command and 0x02 is HCI event. If the first bytes
|
||||
* in the current firmware buffer doesn't start with 0x01 or
|
||||
* the size of remain buffer is smaller than HCI command header,
|
||||
* the firmware file is corrupted and it should stop the patching
|
||||
* process.
|
||||
*/
|
||||
if (remain > HCI_COMMAND_HDR_SIZE && *fw_ptr[0] != 0x01) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid cmd read", hdev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
(*fw_ptr)++;
|
||||
remain--;
|
||||
|
||||
cmd = (struct hci_command_hdr *)(*fw_ptr);
|
||||
*fw_ptr += sizeof(*cmd);
|
||||
remain -= sizeof(*cmd);
|
||||
|
||||
/* Ensure that the remain firmware data is long enough than the length
|
||||
* of command parameter. If not, the firmware file is corrupted.
|
||||
*/
|
||||
if (remain < cmd->plen) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid cmd len", hdev->name);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* If there is a command that loads a patch in the firmware
|
||||
* file, then enable the patch upon success, otherwise just
|
||||
* disable the manufacturer mode, for example patch activation
|
||||
* is not required when the default firmware patch file is used
|
||||
* because there are no patch data to load.
|
||||
*/
|
||||
if (*disable_patch && le16_to_cpu(cmd->opcode) == 0xfc8e)
|
||||
*disable_patch = 0;
|
||||
|
||||
cmd_param = *fw_ptr;
|
||||
*fw_ptr += cmd->plen;
|
||||
remain -= cmd->plen;
|
||||
|
||||
/* This reads the expected events when the above command is sent to the
|
||||
* device. Some vendor commands expects more than one events, for
|
||||
* example command status event followed by vendor specific event.
|
||||
* For this case, it only keeps the last expected event. so the command
|
||||
* can be sent with __hci_cmd_sync_ev() which returns the sk_buff of
|
||||
* last expected event.
|
||||
*/
|
||||
while (remain > HCI_EVENT_HDR_SIZE && *fw_ptr[0] == 0x02) {
|
||||
(*fw_ptr)++;
|
||||
remain--;
|
||||
|
||||
evt = (struct hci_event_hdr *)(*fw_ptr);
|
||||
*fw_ptr += sizeof(*evt);
|
||||
remain -= sizeof(*evt);
|
||||
|
||||
if (remain < evt->plen) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid evt len",
|
||||
hdev->name);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
evt_param = *fw_ptr;
|
||||
*fw_ptr += evt->plen;
|
||||
remain -= evt->plen;
|
||||
}
|
||||
|
||||
/* Every HCI commands in the firmware file has its correspond event.
|
||||
* If event is not found or remain is smaller than zero, the firmware
|
||||
* file is corrupted.
|
||||
*/
|
||||
if (!evt || !evt_param || remain < 0) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid evt read", hdev->name);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, le16_to_cpu(cmd->opcode), cmd->plen,
|
||||
cmd_param, evt->evt, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s sending Intel patch command (0x%4.4x) failed (%ld)",
|
||||
hdev->name, cmd->opcode, PTR_ERR(skb));
|
||||
return -PTR_ERR(skb);
|
||||
}
|
||||
|
||||
/* It ensures that the returned event matches the event data read from
|
||||
* the firmware file. At fist, it checks the length and then
|
||||
* the contents of the event.
|
||||
*/
|
||||
if (skb->len != evt->plen) {
|
||||
BT_ERR("%s mismatch event length (opcode 0x%4.4x)", hdev->name,
|
||||
le16_to_cpu(cmd->opcode));
|
||||
kfree_skb(skb);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (memcmp(skb->data, evt_param, evt->plen)) {
|
||||
BT_ERR("%s mismatch event parameter (opcode 0x%4.4x)",
|
||||
hdev->name, le16_to_cpu(cmd->opcode));
|
||||
kfree_skb(skb);
|
||||
return -EFAULT;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_setup_intel(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
int disable_patch;
|
||||
struct intel_version *ver;
|
||||
|
||||
const u8 mfg_enable[] = { 0x01, 0x00 };
|
||||
const u8 mfg_disable[] = { 0x00, 0x00 };
|
||||
const u8 mfg_reset_deactivate[] = { 0x00, 0x01 };
|
||||
const u8 mfg_reset_activate[] = { 0x00, 0x02 };
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
/* The controller has a bug with the first HCI command sent to it
|
||||
* returning number of completed commands as zero. This would stall the
|
||||
* command processing in the Bluetooth core.
|
||||
*
|
||||
* As a workaround, send HCI Reset command first which will reset the
|
||||
* number of completed commands and allow normal command processing
|
||||
* from now on.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s sending initial HCI reset command failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return -PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
/* Read Intel specific controller version first to allow selection of
|
||||
* which firmware file to load.
|
||||
*
|
||||
* The returned information are hardware variant and revision plus
|
||||
* firmware variant, revision and build number.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s reading Intel fw version command failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return -PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
BT_ERR("%s Intel version event length mismatch", hdev->name);
|
||||
kfree_skb(skb);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ver = (struct intel_version *)skb->data;
|
||||
if (ver->status) {
|
||||
BT_ERR("%s Intel fw version event failed (%02x)", hdev->name,
|
||||
ver->status);
|
||||
kfree_skb(skb);
|
||||
return -bt_to_errno(ver->status);
|
||||
}
|
||||
|
||||
BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
hdev->name, ver->hw_platform, ver->hw_variant,
|
||||
ver->hw_revision, ver->fw_variant, ver->fw_revision,
|
||||
ver->fw_build_num, ver->fw_build_ww, ver->fw_build_yy,
|
||||
ver->fw_patch_num);
|
||||
|
||||
/* fw_patch_num indicates the version of patch the device currently
|
||||
* have. If there is no patch data in the device, it is always 0x00.
|
||||
* So, if it is other than 0x00, no need to patch the deivce again.
|
||||
*/
|
||||
if (ver->fw_patch_num) {
|
||||
BT_INFO("%s: Intel device is already patched. patch num: %02x",
|
||||
hdev->name, ver->fw_patch_num);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Opens the firmware patch file based on the firmware version read
|
||||
* from the controller. If it fails to open the matching firmware
|
||||
* patch file, it tries to open the default firmware patch file.
|
||||
* If no patch file is found, allow the device to operate without
|
||||
* a patch.
|
||||
*/
|
||||
fw = btusb_setup_intel_get_fw(hdev, ver);
|
||||
if (!fw) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
fw_ptr = fw->data;
|
||||
|
||||
/* This Intel specific command enables the manufacturer mode of the
|
||||
* controller.
|
||||
*
|
||||
* Only while this mode is enabled, the driver can download the
|
||||
* firmware patch data and configuration parameters.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s entering Intel manufacturer mode failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
release_firmware(fw);
|
||||
return -PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->data[0]) {
|
||||
u8 evt_status = skb->data[0];
|
||||
BT_ERR("%s enable Intel manufacturer mode event failed (%02x)",
|
||||
hdev->name, evt_status);
|
||||
kfree_skb(skb);
|
||||
release_firmware(fw);
|
||||
return -bt_to_errno(evt_status);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
disable_patch = 1;
|
||||
|
||||
/* The firmware data file consists of list of Intel specific HCI
|
||||
* commands and its expected events. The first byte indicates the
|
||||
* type of the message, either HCI command or HCI event.
|
||||
*
|
||||
* It reads the command and its expected event from the firmware file,
|
||||
* and send to the controller. Once __hci_cmd_sync_ev() returns,
|
||||
* the returned event is compared with the event read from the firmware
|
||||
* file and it will continue until all the messages are downloaded to
|
||||
* the controller.
|
||||
*
|
||||
* Once the firmware patching is completed successfully,
|
||||
* the manufacturer mode is disabled with reset and activating the
|
||||
* downloaded patch.
|
||||
*
|
||||
* If the firmware patching fails, the manufacturer mode is
|
||||
* disabled with reset and deactivating the patch.
|
||||
*
|
||||
* If the default patch file is used, no reset is done when disabling
|
||||
* the manufacturer.
|
||||
*/
|
||||
while (fw->size > fw_ptr - fw->data) {
|
||||
int ret;
|
||||
|
||||
ret = btusb_setup_intel_patching(hdev, fw, &fw_ptr,
|
||||
&disable_patch);
|
||||
if (ret < 0)
|
||||
goto exit_mfg_deactivate;
|
||||
}
|
||||
|
||||
release_firmware(fw);
|
||||
|
||||
if (disable_patch)
|
||||
goto exit_mfg_disable;
|
||||
|
||||
/* Patching completed successfully and disable the manufacturer mode
|
||||
* with reset and activate the downloaded firmware patches.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate),
|
||||
mfg_reset_activate, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return -PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
|
||||
hdev->name);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_mfg_disable:
|
||||
/* Disable the manufacturer mode without reset */
|
||||
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return -PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
|
||||
return 0;
|
||||
|
||||
exit_mfg_deactivate:
|
||||
release_firmware(fw);
|
||||
|
||||
/* Patching failed. Disable the manufacturer mode with reset and
|
||||
* deactivate the downloaded firmware patches.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate),
|
||||
mfg_reset_deactivate, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return -PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
|
||||
hdev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
@ -1022,11 +1413,17 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
SET_HCIDEV_DEV(hdev, &intf->dev);
|
||||
|
||||
hdev->open = btusb_open;
|
||||
hdev->close = btusb_close;
|
||||
hdev->flush = btusb_flush;
|
||||
hdev->send = btusb_send_frame;
|
||||
hdev->notify = btusb_notify;
|
||||
hdev->open = btusb_open;
|
||||
hdev->close = btusb_close;
|
||||
hdev->flush = btusb_flush;
|
||||
hdev->send = btusb_send_frame;
|
||||
hdev->notify = btusb_notify;
|
||||
|
||||
if (id->driver_info & BTUSB_BCM92035)
|
||||
hdev->setup = btusb_setup_bcm92035;
|
||||
|
||||
if (id->driver_info & BTUSB_INTEL)
|
||||
hdev->setup = btusb_setup_intel;
|
||||
|
||||
/* Interface numbers are hardcoded in the specification */
|
||||
data->isoc = usb_ifnum_to_if(data->udev, 1);
|
||||
@ -1065,17 +1462,6 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
data->isoc = NULL;
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_BCM92035) {
|
||||
unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
|
||||
if (skb) {
|
||||
memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
|
||||
skb_queue_tail(&hdev->driver_init, skb);
|
||||
}
|
||||
}
|
||||
|
||||
if (data->isoc) {
|
||||
err = usb_driver_claim_interface(&btusb_driver,
|
||||
data->isoc, data);
|
||||
|
@ -153,6 +153,9 @@ static int h4_recv(struct hci_uart *hu, void *data, int count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
return -EUNATCH;
|
||||
|
||||
ret = hci_recv_stream_fragment(hu->hdev, data, count);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Frame Reassembly Failed");
|
||||
|
@ -260,12 +260,12 @@ static int hci_uart_send_frame(struct sk_buff *skb)
|
||||
|
||||
/* ------ LDISC part ------ */
|
||||
/* hci_uart_tty_open
|
||||
*
|
||||
*
|
||||
* Called when line discipline changed to HCI_UART.
|
||||
*
|
||||
* Arguments:
|
||||
* tty pointer to tty info structure
|
||||
* Return Value:
|
||||
* Return Value:
|
||||
* 0 if success, otherwise error code
|
||||
*/
|
||||
static int hci_uart_tty_open(struct tty_struct *tty)
|
||||
@ -365,15 +365,15 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
|
||||
}
|
||||
|
||||
/* hci_uart_tty_receive()
|
||||
*
|
||||
*
|
||||
* Called by tty low level driver when receive data is
|
||||
* available.
|
||||
*
|
||||
*
|
||||
* Arguments: tty pointer to tty isntance data
|
||||
* data pointer to received data
|
||||
* flags pointer to flags for data
|
||||
* count count of received data in bytes
|
||||
*
|
||||
*
|
||||
* Return Value: None
|
||||
*/
|
||||
static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
|
||||
@ -388,7 +388,10 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *f
|
||||
|
||||
spin_lock(&hu->rx_lock);
|
||||
hu->proto->recv(hu, (void *) data, count);
|
||||
hu->hdev->stat.byte_rx += count;
|
||||
|
||||
if (hu->hdev)
|
||||
hu->hdev->stat.byte_rx += count;
|
||||
|
||||
spin_unlock(&hu->rx_lock);
|
||||
|
||||
tty_unthrottle(tty);
|
||||
|
@ -232,6 +232,31 @@ void proc_comm_connector(struct task_struct *task)
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_coredump_connector(struct task_struct *task)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct proc_event *ev;
|
||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||
struct timespec ts;
|
||||
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
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);
|
||||
ev->what = PROC_EVENT_COREDUMP;
|
||||
ev->event_data.coredump.process_pid = task->pid;
|
||||
ev->event_data.coredump.process_tgid = task->tgid;
|
||||
|
||||
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
|
||||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_exit_connector(struct task_struct *task)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <net/netlink.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/connector.h>
|
||||
#include <linux/slab.h>
|
||||
@ -95,13 +95,13 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
|
||||
if (!netlink_has_listeners(dev->nls, group))
|
||||
return -ESRCH;
|
||||
|
||||
size = NLMSG_SPACE(sizeof(*msg) + msg->len);
|
||||
size = sizeof(*msg) + msg->len;
|
||||
|
||||
skb = alloc_skb(size, gfp_mask);
|
||||
skb = nlmsg_new(size, gfp_mask);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh), 0);
|
||||
nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size, 0);
|
||||
if (!nlh) {
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
@ -124,7 +124,7 @@ static int cn_call_callback(struct sk_buff *skb)
|
||||
{
|
||||
struct cn_callback_entry *i, *cbq = NULL;
|
||||
struct cn_dev *dev = &cdev;
|
||||
struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb));
|
||||
struct cn_msg *msg = nlmsg_data(nlmsg_hdr(skb));
|
||||
struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
|
||||
int err = -ENODEV;
|
||||
|
||||
@ -162,7 +162,7 @@ static void cn_rx_skb(struct sk_buff *__skb)
|
||||
|
||||
skb = skb_get(__skb);
|
||||
|
||||
if (skb->len >= NLMSG_SPACE(0)) {
|
||||
if (skb->len >= NLMSG_HDRLEN) {
|
||||
nlh = nlmsg_hdr(skb);
|
||||
|
||||
if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
|
||||
|
@ -470,8 +470,10 @@ struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase)
|
||||
}
|
||||
|
||||
if (!dca2_tag_map_valid(ioatdca->tag_map)) {
|
||||
dev_err(&pdev->dev, "APICID_TAG_MAP set incorrectly by BIOS, "
|
||||
"disabling DCA\n");
|
||||
WARN_TAINT_ONCE(1, TAINT_FIRMWARE_WORKAROUND,
|
||||
"%s %s: APICID_TAG_MAP set incorrectly by BIOS, disabling DCA\n",
|
||||
dev_driver_string(&pdev->dev),
|
||||
dev_name(&pdev->dev));
|
||||
free_dca_provider(dca);
|
||||
return NULL;
|
||||
}
|
||||
@ -689,7 +691,10 @@ struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase)
|
||||
}
|
||||
|
||||
if (dca3_tag_map_invalid(ioatdca->tag_map)) {
|
||||
dev_err(&pdev->dev, "APICID_TAG_MAP set incorrectly by BIOS, disabling DCA\n");
|
||||
WARN_TAINT_ONCE(1, TAINT_FIRMWARE_WORKAROUND,
|
||||
"%s %s: APICID_TAG_MAP set incorrectly by BIOS, disabling DCA\n",
|
||||
dev_driver_string(&pdev->dev),
|
||||
dev_name(&pdev->dev));
|
||||
free_dca_provider(dca);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -47,9 +47,9 @@ config FIREWIRE_NET
|
||||
tristate "IP networking over 1394"
|
||||
depends on FIREWIRE && INET
|
||||
help
|
||||
This enables IPv4 over IEEE 1394, providing IP connectivity with
|
||||
other implementations of RFC 2734 as found on several operating
|
||||
systems. Multicast support is currently limited.
|
||||
This enables IPv4/IPv6 over IEEE 1394, providing IP connectivity
|
||||
with other implementations of RFC 2734/3146 as found on several
|
||||
operating systems. Multicast support is currently limited.
|
||||
|
||||
To compile this driver as a module, say M here: The module will be
|
||||
called firewire-net.
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* IPv4 over IEEE 1394, per RFC 2734
|
||||
* IPv6 over IEEE 1394, per RFC 3146
|
||||
*
|
||||
* Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com>
|
||||
*
|
||||
@ -28,6 +29,7 @@
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/firewire.h>
|
||||
|
||||
/* rx limits */
|
||||
#define FWNET_MAX_FRAGMENTS 30 /* arbitrary, > TX queue depth */
|
||||
@ -45,6 +47,7 @@
|
||||
|
||||
#define IANA_SPECIFIER_ID 0x00005eU
|
||||
#define RFC2734_SW_VERSION 0x000001U
|
||||
#define RFC3146_SW_VERSION 0x000002U
|
||||
|
||||
#define IEEE1394_GASP_HDR_SIZE 8
|
||||
|
||||
@ -57,32 +60,10 @@
|
||||
#define RFC2374_HDR_LASTFRAG 2 /* last fragment */
|
||||
#define RFC2374_HDR_INTFRAG 3 /* interior fragment */
|
||||
|
||||
#define RFC2734_HW_ADDR_LEN 16
|
||||
|
||||
struct rfc2734_arp {
|
||||
__be16 hw_type; /* 0x0018 */
|
||||
__be16 proto_type; /* 0x0806 */
|
||||
u8 hw_addr_len; /* 16 */
|
||||
u8 ip_addr_len; /* 4 */
|
||||
__be16 opcode; /* ARP Opcode */
|
||||
/* Above is exactly the same format as struct arphdr */
|
||||
|
||||
__be64 s_uniq_id; /* Sender's 64bit EUI */
|
||||
u8 max_rec; /* Sender's max packet size */
|
||||
u8 sspd; /* Sender's max speed */
|
||||
__be16 fifo_hi; /* hi 16bits of sender's FIFO addr */
|
||||
__be32 fifo_lo; /* lo 32bits of sender's FIFO addr */
|
||||
__be32 sip; /* Sender's IP Address */
|
||||
__be32 tip; /* IP Address of requested hw addr */
|
||||
} __packed;
|
||||
|
||||
/* This header format is specific to this driver implementation. */
|
||||
#define FWNET_ALEN 8
|
||||
#define FWNET_HLEN 10
|
||||
struct fwnet_header {
|
||||
u8 h_dest[FWNET_ALEN]; /* destination address */
|
||||
__be16 h_proto; /* packet type ID field */
|
||||
} __packed;
|
||||
static bool fwnet_hwaddr_is_multicast(u8 *ha)
|
||||
{
|
||||
return !!(*ha & 1);
|
||||
}
|
||||
|
||||
/* IPv4 and IPv6 encapsulation header */
|
||||
struct rfc2734_header {
|
||||
@ -191,8 +172,6 @@ struct fwnet_peer {
|
||||
struct list_head peer_link;
|
||||
struct fwnet_device *dev;
|
||||
u64 guid;
|
||||
u64 fifo;
|
||||
__be32 ip;
|
||||
|
||||
/* guarded by dev->lock */
|
||||
struct list_head pd_list; /* received partial datagrams */
|
||||
@ -221,6 +200,15 @@ struct fwnet_packet_task {
|
||||
u8 enqueued;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get fifo address embedded in hwaddr
|
||||
*/
|
||||
static __u64 fwnet_hwaddr_fifo(union fwnet_hwaddr *ha)
|
||||
{
|
||||
return (u64)get_unaligned_be16(&ha->uc.fifo_hi) << 32
|
||||
| get_unaligned_be32(&ha->uc.fifo_lo);
|
||||
}
|
||||
|
||||
/*
|
||||
* saddr == NULL means use device source address.
|
||||
* daddr == NULL means leave destination address (eg unresolved arp).
|
||||
@ -513,10 +501,20 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
|
||||
bool is_broadcast, u16 ether_type)
|
||||
{
|
||||
struct fwnet_device *dev;
|
||||
static const __be64 broadcast_hw = cpu_to_be64(~0ULL);
|
||||
int status;
|
||||
__be64 guid;
|
||||
|
||||
switch (ether_type) {
|
||||
case ETH_P_ARP:
|
||||
case ETH_P_IP:
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case ETH_P_IPV6:
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev = netdev_priv(net);
|
||||
/* Write metadata, and then pass to the receive level */
|
||||
skb->dev = net;
|
||||
@ -524,92 +522,11 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
|
||||
|
||||
/*
|
||||
* Parse the encapsulation header. This actually does the job of
|
||||
* converting to an ethernet frame header, as well as arp
|
||||
* conversion if needed. ARP conversion is easier in this
|
||||
* direction, since we are using ethernet as our backend.
|
||||
* converting to an ethernet-like pseudo frame header.
|
||||
*/
|
||||
/*
|
||||
* If this is an ARP packet, convert it. First, we want to make
|
||||
* use of some of the fields, since they tell us a little bit
|
||||
* about the sending machine.
|
||||
*/
|
||||
if (ether_type == ETH_P_ARP) {
|
||||
struct rfc2734_arp *arp1394;
|
||||
struct arphdr *arp;
|
||||
unsigned char *arp_ptr;
|
||||
u64 fifo_addr;
|
||||
u64 peer_guid;
|
||||
unsigned sspd;
|
||||
u16 max_payload;
|
||||
struct fwnet_peer *peer;
|
||||
unsigned long flags;
|
||||
|
||||
arp1394 = (struct rfc2734_arp *)skb->data;
|
||||
arp = (struct arphdr *)skb->data;
|
||||
arp_ptr = (unsigned char *)(arp + 1);
|
||||
peer_guid = get_unaligned_be64(&arp1394->s_uniq_id);
|
||||
fifo_addr = (u64)get_unaligned_be16(&arp1394->fifo_hi) << 32
|
||||
| get_unaligned_be32(&arp1394->fifo_lo);
|
||||
|
||||
sspd = arp1394->sspd;
|
||||
/* Sanity check. OS X 10.3 PPC reportedly sends 131. */
|
||||
if (sspd > SCODE_3200) {
|
||||
dev_notice(&net->dev, "sspd %x out of range\n", sspd);
|
||||
sspd = SCODE_3200;
|
||||
}
|
||||
max_payload = fwnet_max_payload(arp1394->max_rec, sspd);
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
peer = fwnet_peer_find_by_guid(dev, peer_guid);
|
||||
if (peer) {
|
||||
peer->fifo = fifo_addr;
|
||||
|
||||
if (peer->speed > sspd)
|
||||
peer->speed = sspd;
|
||||
if (peer->max_payload > max_payload)
|
||||
peer->max_payload = max_payload;
|
||||
|
||||
peer->ip = arp1394->sip;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (!peer) {
|
||||
dev_notice(&net->dev,
|
||||
"no peer for ARP packet from %016llx\n",
|
||||
(unsigned long long)peer_guid);
|
||||
goto no_peer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that we're done with the 1394 specific stuff, we'll
|
||||
* need to alter some of the data. Believe it or not, all
|
||||
* that needs to be done is sender_IP_address needs to be
|
||||
* moved, the destination hardware address get stuffed
|
||||
* in and the hardware address length set to 8.
|
||||
*
|
||||
* IMPORTANT: The code below overwrites 1394 specific data
|
||||
* needed above so keep the munging of the data for the
|
||||
* higher level IP stack last.
|
||||
*/
|
||||
|
||||
arp->ar_hln = 8;
|
||||
/* skip over sender unique id */
|
||||
arp_ptr += arp->ar_hln;
|
||||
/* move sender IP addr */
|
||||
put_unaligned(arp1394->sip, (u32 *)arp_ptr);
|
||||
/* skip over sender IP addr */
|
||||
arp_ptr += arp->ar_pln;
|
||||
|
||||
if (arp->ar_op == htons(ARPOP_REQUEST))
|
||||
memset(arp_ptr, 0, sizeof(u64));
|
||||
else
|
||||
memcpy(arp_ptr, net->dev_addr, sizeof(u64));
|
||||
}
|
||||
|
||||
/* Now add the ethernet header. */
|
||||
guid = cpu_to_be64(dev->card->guid);
|
||||
if (dev_hard_header(skb, net, ether_type,
|
||||
is_broadcast ? &broadcast_hw : &guid,
|
||||
is_broadcast ? net->broadcast : net->dev_addr,
|
||||
NULL, skb->len) >= 0) {
|
||||
struct fwnet_header *eth;
|
||||
u16 *rawp;
|
||||
@ -618,7 +535,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
|
||||
skb_reset_mac_header(skb);
|
||||
skb_pull(skb, sizeof(*eth));
|
||||
eth = (struct fwnet_header *)skb_mac_header(skb);
|
||||
if (*eth->h_dest & 1) {
|
||||
if (fwnet_hwaddr_is_multicast(eth->h_dest)) {
|
||||
if (memcmp(eth->h_dest, net->broadcast,
|
||||
net->addr_len) == 0)
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
@ -630,7 +547,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
|
||||
if (memcmp(eth->h_dest, net->dev_addr, net->addr_len))
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
}
|
||||
if (ntohs(eth->h_proto) >= 1536) {
|
||||
if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
|
||||
protocol = eth->h_proto;
|
||||
} else {
|
||||
rawp = (u16 *)skb->data;
|
||||
@ -652,7 +569,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
|
||||
|
||||
return 0;
|
||||
|
||||
no_peer:
|
||||
err:
|
||||
net->stats.rx_errors++;
|
||||
net->stats.rx_dropped++;
|
||||
|
||||
@ -856,7 +773,12 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
|
||||
ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
|
||||
source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
|
||||
|
||||
if (specifier_id == IANA_SPECIFIER_ID && ver == RFC2734_SW_VERSION) {
|
||||
if (specifier_id == IANA_SPECIFIER_ID &&
|
||||
(ver == RFC2734_SW_VERSION
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
|| ver == RFC3146_SW_VERSION
|
||||
#endif
|
||||
)) {
|
||||
buf_ptr += 2;
|
||||
length -= IEEE1394_GASP_HDR_SIZE;
|
||||
fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
|
||||
@ -1059,16 +981,27 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
|
||||
u8 *p;
|
||||
int generation;
|
||||
int node_id;
|
||||
unsigned int sw_version;
|
||||
|
||||
/* ptask->generation may not have been set yet */
|
||||
generation = dev->card->generation;
|
||||
smp_rmb();
|
||||
node_id = dev->card->node_id;
|
||||
|
||||
switch (ptask->skb->protocol) {
|
||||
default:
|
||||
sw_version = RFC2734_SW_VERSION;
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case htons(ETH_P_IPV6):
|
||||
sw_version = RFC3146_SW_VERSION;
|
||||
#endif
|
||||
}
|
||||
|
||||
p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
|
||||
put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
|
||||
put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
|
||||
| RFC2734_SW_VERSION, &p[4]);
|
||||
| sw_version, &p[4]);
|
||||
|
||||
/* We should not transmit if broadcast_channel.valid == 0. */
|
||||
fw_send_request(dev->card, &ptask->transaction,
|
||||
@ -1116,6 +1049,62 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fwnet_fifo_stop(struct fwnet_device *dev)
|
||||
{
|
||||
if (dev->local_fifo == FWNET_NO_FIFO_ADDR)
|
||||
return;
|
||||
|
||||
fw_core_remove_address_handler(&dev->handler);
|
||||
dev->local_fifo = FWNET_NO_FIFO_ADDR;
|
||||
}
|
||||
|
||||
static int fwnet_fifo_start(struct fwnet_device *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
|
||||
return 0;
|
||||
|
||||
dev->handler.length = 4096;
|
||||
dev->handler.address_callback = fwnet_receive_packet;
|
||||
dev->handler.callback_data = dev;
|
||||
|
||||
retval = fw_core_add_address_handler(&dev->handler,
|
||||
&fw_high_memory_region);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
dev->local_fifo = dev->handler.offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __fwnet_broadcast_stop(struct fwnet_device *dev)
|
||||
{
|
||||
unsigned u;
|
||||
|
||||
if (dev->broadcast_state != FWNET_BROADCAST_ERROR) {
|
||||
for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++)
|
||||
kunmap(dev->broadcast_rcv_buffer.pages[u]);
|
||||
fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card);
|
||||
}
|
||||
if (dev->broadcast_rcv_context) {
|
||||
fw_iso_context_destroy(dev->broadcast_rcv_context);
|
||||
dev->broadcast_rcv_context = NULL;
|
||||
}
|
||||
kfree(dev->broadcast_rcv_buffer_ptrs);
|
||||
dev->broadcast_rcv_buffer_ptrs = NULL;
|
||||
dev->broadcast_state = FWNET_BROADCAST_ERROR;
|
||||
}
|
||||
|
||||
static void fwnet_broadcast_stop(struct fwnet_device *dev)
|
||||
{
|
||||
if (dev->broadcast_state == FWNET_BROADCAST_ERROR)
|
||||
return;
|
||||
fw_iso_context_stop(dev->broadcast_rcv_context);
|
||||
__fwnet_broadcast_stop(dev);
|
||||
}
|
||||
|
||||
static int fwnet_broadcast_start(struct fwnet_device *dev)
|
||||
{
|
||||
struct fw_iso_context *context;
|
||||
@ -1124,60 +1113,47 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
|
||||
unsigned max_receive;
|
||||
struct fw_iso_packet packet;
|
||||
unsigned long offset;
|
||||
void **ptrptr;
|
||||
unsigned u;
|
||||
|
||||
if (dev->local_fifo == FWNET_NO_FIFO_ADDR) {
|
||||
dev->handler.length = 4096;
|
||||
dev->handler.address_callback = fwnet_receive_packet;
|
||||
dev->handler.callback_data = dev;
|
||||
|
||||
retval = fw_core_add_address_handler(&dev->handler,
|
||||
&fw_high_memory_region);
|
||||
if (retval < 0)
|
||||
goto failed_initial;
|
||||
|
||||
dev->local_fifo = dev->handler.offset;
|
||||
}
|
||||
if (dev->broadcast_state != FWNET_BROADCAST_ERROR)
|
||||
return 0;
|
||||
|
||||
max_receive = 1U << (dev->card->max_receive + 1);
|
||||
num_packets = (FWNET_ISO_PAGE_COUNT * PAGE_SIZE) / max_receive;
|
||||
|
||||
if (!dev->broadcast_rcv_context) {
|
||||
void **ptrptr;
|
||||
|
||||
context = fw_iso_context_create(dev->card,
|
||||
FW_ISO_CONTEXT_RECEIVE, IEEE1394_BROADCAST_CHANNEL,
|
||||
dev->card->link_speed, 8, fwnet_receive_broadcast, dev);
|
||||
if (IS_ERR(context)) {
|
||||
retval = PTR_ERR(context);
|
||||
goto failed_context_create;
|
||||
}
|
||||
|
||||
retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer,
|
||||
dev->card, FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE);
|
||||
if (retval < 0)
|
||||
goto failed_buffer_init;
|
||||
|
||||
ptrptr = kmalloc(sizeof(void *) * num_packets, GFP_KERNEL);
|
||||
if (!ptrptr) {
|
||||
retval = -ENOMEM;
|
||||
goto failed_ptrs_alloc;
|
||||
}
|
||||
|
||||
dev->broadcast_rcv_buffer_ptrs = ptrptr;
|
||||
for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) {
|
||||
void *ptr;
|
||||
unsigned v;
|
||||
|
||||
ptr = kmap(dev->broadcast_rcv_buffer.pages[u]);
|
||||
for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++)
|
||||
*ptrptr++ = (void *)
|
||||
((char *)ptr + v * max_receive);
|
||||
}
|
||||
dev->broadcast_rcv_context = context;
|
||||
} else {
|
||||
context = dev->broadcast_rcv_context;
|
||||
ptrptr = kmalloc(sizeof(void *) * num_packets, GFP_KERNEL);
|
||||
if (!ptrptr) {
|
||||
retval = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
dev->broadcast_rcv_buffer_ptrs = ptrptr;
|
||||
|
||||
context = fw_iso_context_create(dev->card, FW_ISO_CONTEXT_RECEIVE,
|
||||
IEEE1394_BROADCAST_CHANNEL,
|
||||
dev->card->link_speed, 8,
|
||||
fwnet_receive_broadcast, dev);
|
||||
if (IS_ERR(context)) {
|
||||
retval = PTR_ERR(context);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer, dev->card,
|
||||
FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE);
|
||||
if (retval < 0)
|
||||
goto failed;
|
||||
|
||||
dev->broadcast_state = FWNET_BROADCAST_STOPPED;
|
||||
|
||||
for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) {
|
||||
void *ptr;
|
||||
unsigned v;
|
||||
|
||||
ptr = kmap(dev->broadcast_rcv_buffer.pages[u]);
|
||||
for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++)
|
||||
*ptrptr++ = (void *) ((char *)ptr + v * max_receive);
|
||||
}
|
||||
dev->broadcast_rcv_context = context;
|
||||
|
||||
packet.payload_length = max_receive;
|
||||
packet.interrupt = 1;
|
||||
@ -1191,7 +1167,7 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
|
||||
retval = fw_iso_context_queue(context, &packet,
|
||||
&dev->broadcast_rcv_buffer, offset);
|
||||
if (retval < 0)
|
||||
goto failed_rcv_queue;
|
||||
goto failed;
|
||||
|
||||
offset += max_receive;
|
||||
}
|
||||
@ -1201,7 +1177,7 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
|
||||
retval = fw_iso_context_start(context, -1, 0,
|
||||
FW_ISO_CONTEXT_MATCH_ALL_TAGS); /* ??? sync */
|
||||
if (retval < 0)
|
||||
goto failed_rcv_queue;
|
||||
goto failed;
|
||||
|
||||
/* FIXME: adjust it according to the min. speed of all known peers? */
|
||||
dev->broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100
|
||||
@ -1210,19 +1186,8 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
|
||||
|
||||
return 0;
|
||||
|
||||
failed_rcv_queue:
|
||||
kfree(dev->broadcast_rcv_buffer_ptrs);
|
||||
dev->broadcast_rcv_buffer_ptrs = NULL;
|
||||
failed_ptrs_alloc:
|
||||
fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card);
|
||||
failed_buffer_init:
|
||||
fw_iso_context_destroy(context);
|
||||
dev->broadcast_rcv_context = NULL;
|
||||
failed_context_create:
|
||||
fw_core_remove_address_handler(&dev->handler);
|
||||
failed_initial:
|
||||
dev->local_fifo = FWNET_NO_FIFO_ADDR;
|
||||
|
||||
failed:
|
||||
__fwnet_broadcast_stop(dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -1240,11 +1205,10 @@ static int fwnet_open(struct net_device *net)
|
||||
struct fwnet_device *dev = netdev_priv(net);
|
||||
int ret;
|
||||
|
||||
if (dev->broadcast_state == FWNET_BROADCAST_ERROR) {
|
||||
ret = fwnet_broadcast_start(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = fwnet_broadcast_start(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
netif_start_queue(net);
|
||||
|
||||
spin_lock_irq(&dev->lock);
|
||||
@ -1257,9 +1221,10 @@ static int fwnet_open(struct net_device *net)
|
||||
/* ifdown */
|
||||
static int fwnet_stop(struct net_device *net)
|
||||
{
|
||||
netif_stop_queue(net);
|
||||
struct fwnet_device *dev = netdev_priv(net);
|
||||
|
||||
/* Deallocate iso context for use by other applications? */
|
||||
netif_stop_queue(net);
|
||||
fwnet_broadcast_stop(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1299,19 +1264,27 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
||||
* We might need to rebuild the header on tx failure.
|
||||
*/
|
||||
memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
|
||||
skb_pull(skb, sizeof(hdr_buf));
|
||||
|
||||
proto = hdr_buf.h_proto;
|
||||
|
||||
switch (proto) {
|
||||
case htons(ETH_P_ARP):
|
||||
case htons(ETH_P_IP):
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case htons(ETH_P_IPV6):
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
skb_pull(skb, sizeof(hdr_buf));
|
||||
dg_size = skb->len;
|
||||
|
||||
/*
|
||||
* Set the transmission type for the packet. ARP packets and IP
|
||||
* broadcast packets are sent via GASP.
|
||||
*/
|
||||
if (memcmp(hdr_buf.h_dest, net->broadcast, FWNET_ALEN) == 0
|
||||
|| proto == htons(ETH_P_ARP)
|
||||
|| (proto == htons(ETH_P_IP)
|
||||
&& IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
|
||||
if (fwnet_hwaddr_is_multicast(hdr_buf.h_dest)) {
|
||||
max_payload = dev->broadcast_xmt_max_payload;
|
||||
datagram_label_ptr = &dev->broadcast_xmt_datagramlabel;
|
||||
|
||||
@ -1320,11 +1293,12 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
||||
ptask->dest_node = IEEE1394_ALL_NODES;
|
||||
ptask->speed = SCODE_100;
|
||||
} else {
|
||||
__be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest);
|
||||
union fwnet_hwaddr *ha = (union fwnet_hwaddr *)hdr_buf.h_dest;
|
||||
__be64 guid = get_unaligned(&ha->uc.uniq_id);
|
||||
u8 generation;
|
||||
|
||||
peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid));
|
||||
if (!peer || peer->fifo == FWNET_NO_FIFO_ADDR)
|
||||
if (!peer)
|
||||
goto fail;
|
||||
|
||||
generation = peer->generation;
|
||||
@ -1332,32 +1306,12 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
||||
max_payload = peer->max_payload;
|
||||
datagram_label_ptr = &peer->datagram_label;
|
||||
|
||||
ptask->fifo_addr = peer->fifo;
|
||||
ptask->fifo_addr = fwnet_hwaddr_fifo(ha);
|
||||
ptask->generation = generation;
|
||||
ptask->dest_node = dest_node;
|
||||
ptask->speed = peer->speed;
|
||||
}
|
||||
|
||||
/* If this is an ARP packet, convert it */
|
||||
if (proto == htons(ETH_P_ARP)) {
|
||||
struct arphdr *arp = (struct arphdr *)skb->data;
|
||||
unsigned char *arp_ptr = (unsigned char *)(arp + 1);
|
||||
struct rfc2734_arp *arp1394 = (struct rfc2734_arp *)skb->data;
|
||||
__be32 ipaddr;
|
||||
|
||||
ipaddr = get_unaligned((__be32 *)(arp_ptr + FWNET_ALEN));
|
||||
|
||||
arp1394->hw_addr_len = RFC2734_HW_ADDR_LEN;
|
||||
arp1394->max_rec = dev->card->max_receive;
|
||||
arp1394->sspd = dev->card->link_speed;
|
||||
|
||||
put_unaligned_be16(dev->local_fifo >> 32,
|
||||
&arp1394->fifo_hi);
|
||||
put_unaligned_be32(dev->local_fifo & 0xffffffff,
|
||||
&arp1394->fifo_lo);
|
||||
put_unaligned(ipaddr, &arp1394->sip);
|
||||
}
|
||||
|
||||
ptask->hdr.w0 = 0;
|
||||
ptask->hdr.w1 = 0;
|
||||
ptask->skb = skb;
|
||||
@ -1472,8 +1426,6 @@ static int fwnet_add_peer(struct fwnet_device *dev,
|
||||
|
||||
peer->dev = dev;
|
||||
peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
|
||||
peer->fifo = FWNET_NO_FIFO_ADDR;
|
||||
peer->ip = 0;
|
||||
INIT_LIST_HEAD(&peer->pd_list);
|
||||
peer->pdg_size = 0;
|
||||
peer->datagram_label = 0;
|
||||
@ -1503,6 +1455,7 @@ static int fwnet_probe(struct device *_dev)
|
||||
struct fwnet_device *dev;
|
||||
unsigned max_mtu;
|
||||
int ret;
|
||||
union fwnet_hwaddr *ha;
|
||||
|
||||
mutex_lock(&fwnet_device_mutex);
|
||||
|
||||
@ -1533,6 +1486,11 @@ static int fwnet_probe(struct device *_dev)
|
||||
dev->card = card;
|
||||
dev->netdev = net;
|
||||
|
||||
ret = fwnet_fifo_start(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
dev->local_fifo = dev->handler.offset;
|
||||
|
||||
/*
|
||||
* Use the RFC 2734 default 1500 octets or the maximum payload
|
||||
* as initial MTU
|
||||
@ -1542,24 +1500,31 @@ static int fwnet_probe(struct device *_dev)
|
||||
net->mtu = min(1500U, max_mtu);
|
||||
|
||||
/* Set our hardware address while we're at it */
|
||||
put_unaligned_be64(card->guid, net->dev_addr);
|
||||
put_unaligned_be64(~0ULL, net->broadcast);
|
||||
ha = (union fwnet_hwaddr *)net->dev_addr;
|
||||
put_unaligned_be64(card->guid, &ha->uc.uniq_id);
|
||||
ha->uc.max_rec = dev->card->max_receive;
|
||||
ha->uc.sspd = dev->card->link_speed;
|
||||
put_unaligned_be16(dev->local_fifo >> 32, &ha->uc.fifo_hi);
|
||||
put_unaligned_be32(dev->local_fifo & 0xffffffff, &ha->uc.fifo_lo);
|
||||
|
||||
memset(net->broadcast, -1, net->addr_len);
|
||||
|
||||
ret = register_netdev(net);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
list_add_tail(&dev->dev_link, &fwnet_device_list);
|
||||
dev_notice(&net->dev, "IPv4 over IEEE 1394 on card %s\n",
|
||||
dev_notice(&net->dev, "IP over IEEE 1394 on card %s\n",
|
||||
dev_name(card->device));
|
||||
have_dev:
|
||||
ret = fwnet_add_peer(dev, unit, device);
|
||||
if (ret && allocated_netdev) {
|
||||
unregister_netdev(net);
|
||||
list_del(&dev->dev_link);
|
||||
}
|
||||
out:
|
||||
if (ret && allocated_netdev)
|
||||
fwnet_fifo_stop(dev);
|
||||
free_netdev(net);
|
||||
}
|
||||
|
||||
mutex_unlock(&fwnet_device_mutex);
|
||||
|
||||
@ -1592,22 +1557,14 @@ static int fwnet_remove(struct device *_dev)
|
||||
mutex_lock(&fwnet_device_mutex);
|
||||
|
||||
net = dev->netdev;
|
||||
if (net && peer->ip)
|
||||
arp_invalidate(net, peer->ip);
|
||||
|
||||
fwnet_remove_peer(peer, dev);
|
||||
|
||||
if (list_empty(&dev->peer_list)) {
|
||||
unregister_netdev(net);
|
||||
|
||||
if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
|
||||
fw_core_remove_address_handler(&dev->handler);
|
||||
if (dev->broadcast_rcv_context) {
|
||||
fw_iso_context_stop(dev->broadcast_rcv_context);
|
||||
fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer,
|
||||
dev->card);
|
||||
fw_iso_context_destroy(dev->broadcast_rcv_context);
|
||||
}
|
||||
fwnet_fifo_stop(dev);
|
||||
|
||||
for (i = 0; dev->queued_datagrams && i < 5; i++)
|
||||
ssleep(1);
|
||||
WARN_ON(dev->queued_datagrams);
|
||||
@ -1646,6 +1603,14 @@ static const struct ieee1394_device_id fwnet_id_table[] = {
|
||||
.specifier_id = IANA_SPECIFIER_ID,
|
||||
.version = RFC2734_SW_VERSION,
|
||||
},
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_SPECIFIER_ID |
|
||||
IEEE1394_MATCH_VERSION,
|
||||
.specifier_id = IANA_SPECIFIER_ID,
|
||||
.version = RFC3146_SW_VERSION,
|
||||
},
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -1683,6 +1648,30 @@ static struct fw_descriptor rfc2374_unit_directory = {
|
||||
.data = rfc2374_unit_directory_data
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static const u32 rfc3146_unit_directory_data[] = {
|
||||
0x00040000, /* directory_length */
|
||||
0x1200005e, /* unit_specifier_id: IANA */
|
||||
0x81000003, /* textual descriptor offset */
|
||||
0x13000002, /* unit_sw_version: RFC 3146 */
|
||||
0x81000005, /* textual descriptor offset */
|
||||
0x00030000, /* descriptor_length */
|
||||
0x00000000, /* text */
|
||||
0x00000000, /* minimal ASCII, en */
|
||||
0x49414e41, /* I A N A */
|
||||
0x00030000, /* descriptor_length */
|
||||
0x00000000, /* text */
|
||||
0x00000000, /* minimal ASCII, en */
|
||||
0x49507636, /* I P v 6 */
|
||||
};
|
||||
|
||||
static struct fw_descriptor rfc3146_unit_directory = {
|
||||
.length = ARRAY_SIZE(rfc3146_unit_directory_data),
|
||||
.key = (CSR_DIRECTORY | CSR_UNIT) << 24,
|
||||
.data = rfc3146_unit_directory_data
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __init fwnet_init(void)
|
||||
{
|
||||
int err;
|
||||
@ -1691,11 +1680,17 @@ static int __init fwnet_init(void)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
err = fw_core_add_descriptor(&rfc3146_unit_directory);
|
||||
if (err)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
fwnet_packet_task_cache = kmem_cache_create("packet_task",
|
||||
sizeof(struct fwnet_packet_task), 0, 0, NULL);
|
||||
if (!fwnet_packet_task_cache) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
err = driver_register(&fwnet_driver.driver);
|
||||
@ -1703,7 +1698,11 @@ static int __init fwnet_init(void)
|
||||
return 0;
|
||||
|
||||
kmem_cache_destroy(fwnet_packet_task_cache);
|
||||
out2:
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
fw_core_remove_descriptor(&rfc3146_unit_directory);
|
||||
out:
|
||||
#endif
|
||||
fw_core_remove_descriptor(&rfc2374_unit_directory);
|
||||
|
||||
return err;
|
||||
@ -1714,11 +1713,14 @@ static void __exit fwnet_cleanup(void)
|
||||
{
|
||||
driver_unregister(&fwnet_driver.driver);
|
||||
kmem_cache_destroy(fwnet_packet_task_cache);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
fw_core_remove_descriptor(&rfc3146_unit_directory);
|
||||
#endif
|
||||
fw_core_remove_descriptor(&rfc2374_unit_directory);
|
||||
}
|
||||
module_exit(fwnet_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>");
|
||||
MODULE_DESCRIPTION("IPv4 over IEEE1394 as per RFC 2734");
|
||||
MODULE_DESCRIPTION("IP over IEEE1394 as per RFC 2734/3146");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DEVICE_TABLE(ieee1394, fwnet_id_table);
|
||||
|
@ -511,12 +511,16 @@ static unsigned int select_ntuple(struct c4iw_dev *dev, struct dst_entry *dst,
|
||||
static int send_connect(struct c4iw_ep *ep)
|
||||
{
|
||||
struct cpl_act_open_req *req;
|
||||
struct cpl_t5_act_open_req *t5_req;
|
||||
struct sk_buff *skb;
|
||||
u64 opt0;
|
||||
u32 opt2;
|
||||
unsigned int mtu_idx;
|
||||
int wscale;
|
||||
int wrlen = roundup(sizeof *req, 16);
|
||||
int size = is_t4(ep->com.dev->rdev.lldi.adapter_type) ?
|
||||
sizeof(struct cpl_act_open_req) :
|
||||
sizeof(struct cpl_t5_act_open_req);
|
||||
int wrlen = roundup(size, 16);
|
||||
|
||||
PDBG("%s ep %p atid %u\n", __func__, ep, ep->atid);
|
||||
|
||||
@ -552,17 +556,36 @@ static int send_connect(struct c4iw_ep *ep)
|
||||
opt2 |= WND_SCALE_EN(1);
|
||||
t4_set_arp_err_handler(skb, NULL, act_open_req_arp_failure);
|
||||
|
||||
req = (struct cpl_act_open_req *) skb_put(skb, wrlen);
|
||||
INIT_TP_WR(req, 0);
|
||||
OPCODE_TID(req) = cpu_to_be32(
|
||||
MK_OPCODE_TID(CPL_ACT_OPEN_REQ, ((ep->rss_qid<<14)|ep->atid)));
|
||||
req->local_port = ep->com.local_addr.sin_port;
|
||||
req->peer_port = ep->com.remote_addr.sin_port;
|
||||
req->local_ip = ep->com.local_addr.sin_addr.s_addr;
|
||||
req->peer_ip = ep->com.remote_addr.sin_addr.s_addr;
|
||||
req->opt0 = cpu_to_be64(opt0);
|
||||
req->params = cpu_to_be32(select_ntuple(ep->com.dev, ep->dst, ep->l2t));
|
||||
req->opt2 = cpu_to_be32(opt2);
|
||||
if (is_t4(ep->com.dev->rdev.lldi.adapter_type)) {
|
||||
req = (struct cpl_act_open_req *) skb_put(skb, wrlen);
|
||||
INIT_TP_WR(req, 0);
|
||||
OPCODE_TID(req) = cpu_to_be32(
|
||||
MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
|
||||
((ep->rss_qid << 14) | ep->atid)));
|
||||
req->local_port = ep->com.local_addr.sin_port;
|
||||
req->peer_port = ep->com.remote_addr.sin_port;
|
||||
req->local_ip = ep->com.local_addr.sin_addr.s_addr;
|
||||
req->peer_ip = ep->com.remote_addr.sin_addr.s_addr;
|
||||
req->opt0 = cpu_to_be64(opt0);
|
||||
req->params = cpu_to_be32(select_ntuple(ep->com.dev,
|
||||
ep->dst, ep->l2t));
|
||||
req->opt2 = cpu_to_be32(opt2);
|
||||
} else {
|
||||
t5_req = (struct cpl_t5_act_open_req *) skb_put(skb, wrlen);
|
||||
INIT_TP_WR(t5_req, 0);
|
||||
OPCODE_TID(t5_req) = cpu_to_be32(
|
||||
MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
|
||||
((ep->rss_qid << 14) | ep->atid)));
|
||||
t5_req->local_port = ep->com.local_addr.sin_port;
|
||||
t5_req->peer_port = ep->com.remote_addr.sin_port;
|
||||
t5_req->local_ip = ep->com.local_addr.sin_addr.s_addr;
|
||||
t5_req->peer_ip = ep->com.remote_addr.sin_addr.s_addr;
|
||||
t5_req->opt0 = cpu_to_be64(opt0);
|
||||
t5_req->params = cpu_to_be64(V_FILTER_TUPLE(
|
||||
select_ntuple(ep->com.dev, ep->dst, ep->l2t)));
|
||||
t5_req->opt2 = cpu_to_be32(opt2);
|
||||
}
|
||||
|
||||
set_bit(ACT_OPEN_REQ, &ep->com.history);
|
||||
return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
|
||||
}
|
||||
@ -1676,9 +1699,9 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
|
||||
case CPL_ERR_CONN_TIMEDOUT:
|
||||
break;
|
||||
case CPL_ERR_TCAM_FULL:
|
||||
dev->rdev.stats.tcam_full++;
|
||||
if (dev->rdev.lldi.enable_fw_ofld_conn) {
|
||||
mutex_lock(&dev->rdev.stats.lock);
|
||||
dev->rdev.stats.tcam_full++;
|
||||
mutex_unlock(&dev->rdev.stats.lock);
|
||||
send_fw_act_open_req(ep,
|
||||
GET_TID_TID(GET_AOPEN_ATID(
|
||||
@ -2875,12 +2898,14 @@ static int deferred_fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb)
|
||||
static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos)
|
||||
{
|
||||
u32 l2info;
|
||||
u16 vlantag, len, hdr_len;
|
||||
u16 vlantag, len, hdr_len, eth_hdr_len;
|
||||
u8 intf;
|
||||
struct cpl_rx_pkt *cpl = cplhdr(skb);
|
||||
struct cpl_pass_accept_req *req;
|
||||
struct tcp_options_received tmp_opt;
|
||||
struct c4iw_dev *dev;
|
||||
|
||||
dev = *((struct c4iw_dev **) (skb->cb + sizeof(void *)));
|
||||
/* Store values from cpl_rx_pkt in temporary location. */
|
||||
vlantag = (__force u16) cpl->vlan;
|
||||
len = (__force u16) cpl->len;
|
||||
@ -2896,7 +2921,7 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos)
|
||||
*/
|
||||
memset(&tmp_opt, 0, sizeof(tmp_opt));
|
||||
tcp_clear_options(&tmp_opt);
|
||||
tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL);
|
||||
tcp_parse_options(skb, &tmp_opt, 0, NULL);
|
||||
|
||||
req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req));
|
||||
memset(req, 0, sizeof(*req));
|
||||
@ -2904,14 +2929,16 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos)
|
||||
V_SYN_MAC_IDX(G_RX_MACIDX(
|
||||
(__force int) htonl(l2info))) |
|
||||
F_SYN_XACT_MATCH);
|
||||
eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ?
|
||||
G_RX_ETHHDR_LEN((__force int) htonl(l2info)) :
|
||||
G_RX_T5_ETHHDR_LEN((__force int) htonl(l2info));
|
||||
req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN(
|
||||
(__force int) htonl(l2info))) |
|
||||
V_TCP_HDR_LEN(G_RX_TCPHDR_LEN(
|
||||
(__force int) htons(hdr_len))) |
|
||||
V_IP_HDR_LEN(G_RX_IPHDR_LEN(
|
||||
(__force int) htons(hdr_len))) |
|
||||
V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(
|
||||
(__force int) htonl(l2info))));
|
||||
V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(eth_hdr_len)));
|
||||
req->vlan = (__force __be16) vlantag;
|
||||
req->len = (__force __be16) len;
|
||||
req->tos_stid = cpu_to_be32(PASS_OPEN_TID(stid) |
|
||||
@ -2999,7 +3026,7 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)
|
||||
u16 window;
|
||||
struct port_info *pi;
|
||||
struct net_device *pdev;
|
||||
u16 rss_qid;
|
||||
u16 rss_qid, eth_hdr_len;
|
||||
int step;
|
||||
u32 tx_chan;
|
||||
struct neighbour *neigh;
|
||||
@ -3028,7 +3055,10 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)
|
||||
goto reject;
|
||||
}
|
||||
|
||||
if (G_RX_ETHHDR_LEN(ntohl(cpl->l2info)) == ETH_HLEN) {
|
||||
eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ?
|
||||
G_RX_ETHHDR_LEN(htonl(cpl->l2info)) :
|
||||
G_RX_T5_ETHHDR_LEN(htonl(cpl->l2info));
|
||||
if (eth_hdr_len == ETH_HLEN) {
|
||||
eh = (struct ethhdr *)(req + 1);
|
||||
iph = (struct iphdr *)(eh + 1);
|
||||
} else {
|
||||
|
@ -41,10 +41,20 @@
|
||||
#define DRV_VERSION "0.1"
|
||||
|
||||
MODULE_AUTHOR("Steve Wise");
|
||||
MODULE_DESCRIPTION("Chelsio T4 RDMA Driver");
|
||||
MODULE_DESCRIPTION("Chelsio T4/T5 RDMA Driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
|
||||
static int allow_db_fc_on_t5;
|
||||
module_param(allow_db_fc_on_t5, int, 0644);
|
||||
MODULE_PARM_DESC(allow_db_fc_on_t5,
|
||||
"Allow DB Flow Control on T5 (default = 0)");
|
||||
|
||||
static int allow_db_coalescing_on_t5;
|
||||
module_param(allow_db_coalescing_on_t5, int, 0644);
|
||||
MODULE_PARM_DESC(allow_db_coalescing_on_t5,
|
||||
"Allow DB Coalescing on T5 (default = 0)");
|
||||
|
||||
struct uld_ctx {
|
||||
struct list_head entry;
|
||||
struct cxgb4_lld_info lldi;
|
||||
@ -614,7 +624,7 @@ static int rdma_supported(const struct cxgb4_lld_info *infop)
|
||||
{
|
||||
return infop->vr->stag.size > 0 && infop->vr->pbl.size > 0 &&
|
||||
infop->vr->rq.size > 0 && infop->vr->qp.size > 0 &&
|
||||
infop->vr->cq.size > 0 && infop->vr->ocq.size > 0;
|
||||
infop->vr->cq.size > 0;
|
||||
}
|
||||
|
||||
static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
|
||||
@ -627,6 +637,22 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
|
||||
pci_name(infop->pdev));
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
if (!ocqp_supported(infop))
|
||||
pr_info("%s: On-Chip Queues not supported on this device.\n",
|
||||
pci_name(infop->pdev));
|
||||
|
||||
if (!is_t4(infop->adapter_type)) {
|
||||
if (!allow_db_fc_on_t5) {
|
||||
db_fc_threshold = 100000;
|
||||
pr_info("DB Flow Control Disabled.\n");
|
||||
}
|
||||
|
||||
if (!allow_db_coalescing_on_t5) {
|
||||
db_coalescing_threshold = -1;
|
||||
pr_info("DB Coalescing Disabled.\n");
|
||||
}
|
||||
}
|
||||
|
||||
devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp));
|
||||
if (!devp) {
|
||||
printk(KERN_ERR MOD "Cannot allocate ib device\n");
|
||||
@ -678,8 +704,8 @@ static void *c4iw_uld_add(const struct cxgb4_lld_info *infop)
|
||||
int i;
|
||||
|
||||
if (!vers_printed++)
|
||||
printk(KERN_INFO MOD "Chelsio T4 RDMA Driver - version %s\n",
|
||||
DRV_VERSION);
|
||||
pr_info("Chelsio T4/T5 RDMA Driver - version %s\n",
|
||||
DRV_VERSION);
|
||||
|
||||
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
|
@ -162,7 +162,7 @@ static inline int c4iw_num_stags(struct c4iw_rdev *rdev)
|
||||
return min((int)T4_MAX_NUM_STAG, (int)(rdev->lldi.vr->stag.size >> 5));
|
||||
}
|
||||
|
||||
#define C4IW_WR_TO (10*HZ)
|
||||
#define C4IW_WR_TO (30*HZ)
|
||||
|
||||
struct c4iw_wr_wait {
|
||||
struct completion completion;
|
||||
@ -369,7 +369,6 @@ struct c4iw_fr_page_list {
|
||||
DEFINE_DMA_UNMAP_ADDR(mapping);
|
||||
dma_addr_t dma_addr;
|
||||
struct c4iw_dev *dev;
|
||||
int size;
|
||||
};
|
||||
|
||||
static inline struct c4iw_fr_page_list *to_c4iw_fr_page_list(
|
||||
@ -817,6 +816,15 @@ static inline int compute_wscale(int win)
|
||||
return wscale;
|
||||
}
|
||||
|
||||
static inline int ocqp_supported(const struct cxgb4_lld_info *infop)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(CONFIG_PPC64)
|
||||
return infop->vr->ocq.size > 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
u32 c4iw_id_alloc(struct c4iw_id_table *alloc);
|
||||
void c4iw_id_free(struct c4iw_id_table *alloc, u32 obj);
|
||||
int c4iw_id_table_alloc(struct c4iw_id_table *alloc, u32 start, u32 num,
|
||||
@ -930,6 +938,8 @@ extern struct cxgb4_client t4c_client;
|
||||
extern c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS];
|
||||
extern int c4iw_max_read_depth;
|
||||
extern int db_fc_threshold;
|
||||
extern int db_coalescing_threshold;
|
||||
extern int use_dsgl;
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -30,16 +30,76 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <rdma/ib_umem.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include "iw_cxgb4.h"
|
||||
|
||||
int use_dsgl = 1;
|
||||
module_param(use_dsgl, int, 0644);
|
||||
MODULE_PARM_DESC(use_dsgl, "Use DSGL for PBL/FastReg (default=1)");
|
||||
|
||||
#define T4_ULPTX_MIN_IO 32
|
||||
#define C4IW_MAX_INLINE_SIZE 96
|
||||
#define T4_ULPTX_MAX_DMA 1024
|
||||
#define C4IW_INLINE_THRESHOLD 128
|
||||
|
||||
static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
|
||||
void *data)
|
||||
static int inline_threshold = C4IW_INLINE_THRESHOLD;
|
||||
module_param(inline_threshold, int, 0644);
|
||||
MODULE_PARM_DESC(inline_threshold, "inline vs dsgl threshold (default=128)");
|
||||
|
||||
static int _c4iw_write_mem_dma_aligned(struct c4iw_rdev *rdev, u32 addr,
|
||||
u32 len, dma_addr_t data, int wait)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct ulp_mem_io *req;
|
||||
struct ulptx_sgl *sgl;
|
||||
u8 wr_len;
|
||||
int ret = 0;
|
||||
struct c4iw_wr_wait wr_wait;
|
||||
|
||||
addr &= 0x7FFFFFF;
|
||||
|
||||
if (wait)
|
||||
c4iw_init_wr_wait(&wr_wait);
|
||||
wr_len = roundup(sizeof(*req) + sizeof(*sgl), 16);
|
||||
|
||||
skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0);
|
||||
|
||||
req = (struct ulp_mem_io *)__skb_put(skb, wr_len);
|
||||
memset(req, 0, wr_len);
|
||||
INIT_ULPTX_WR(req, wr_len, 0, 0);
|
||||
req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR) |
|
||||
(wait ? FW_WR_COMPL(1) : 0));
|
||||
req->wr.wr_lo = wait ? (__force __be64)&wr_wait : 0;
|
||||
req->wr.wr_mid = cpu_to_be32(FW_WR_LEN16(DIV_ROUND_UP(wr_len, 16)));
|
||||
req->cmd = cpu_to_be32(ULPTX_CMD(ULP_TX_MEM_WRITE));
|
||||
req->cmd |= cpu_to_be32(V_T5_ULP_MEMIO_ORDER(1));
|
||||
req->dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN(len>>5));
|
||||
req->len16 = cpu_to_be32(DIV_ROUND_UP(wr_len-sizeof(req->wr), 16));
|
||||
req->lock_addr = cpu_to_be32(ULP_MEMIO_ADDR(addr));
|
||||
|
||||
sgl = (struct ulptx_sgl *)(req + 1);
|
||||
sgl->cmd_nsge = cpu_to_be32(ULPTX_CMD(ULP_TX_SC_DSGL) |
|
||||
ULPTX_NSGE(1));
|
||||
sgl->len0 = cpu_to_be32(len);
|
||||
sgl->addr0 = cpu_to_be64(data);
|
||||
|
||||
ret = c4iw_ofld_send(rdev, skb);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (wait)
|
||||
ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _c4iw_write_mem_inline(struct c4iw_rdev *rdev, u32 addr, u32 len,
|
||||
void *data)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct ulp_mem_io *req;
|
||||
@ -47,6 +107,12 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
|
||||
u8 wr_len, *to_dp, *from_dp;
|
||||
int copy_len, num_wqe, i, ret = 0;
|
||||
struct c4iw_wr_wait wr_wait;
|
||||
__be32 cmd = cpu_to_be32(ULPTX_CMD(ULP_TX_MEM_WRITE));
|
||||
|
||||
if (is_t4(rdev->lldi.adapter_type))
|
||||
cmd |= cpu_to_be32(ULP_MEMIO_ORDER(1));
|
||||
else
|
||||
cmd |= cpu_to_be32(V_T5_ULP_MEMIO_IMM(1));
|
||||
|
||||
addr &= 0x7FFFFFF;
|
||||
PDBG("%s addr 0x%x len %u\n", __func__, addr, len);
|
||||
@ -77,7 +143,7 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
|
||||
req->wr.wr_mid = cpu_to_be32(
|
||||
FW_WR_LEN16(DIV_ROUND_UP(wr_len, 16)));
|
||||
|
||||
req->cmd = cpu_to_be32(ULPTX_CMD(ULP_TX_MEM_WRITE) | (1<<23));
|
||||
req->cmd = cmd;
|
||||
req->dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN(
|
||||
DIV_ROUND_UP(copy_len, T4_ULPTX_MIN_IO)));
|
||||
req->len16 = cpu_to_be32(DIV_ROUND_UP(wr_len-sizeof(req->wr),
|
||||
@ -107,6 +173,67 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len, void *data)
|
||||
{
|
||||
u32 remain = len;
|
||||
u32 dmalen;
|
||||
int ret = 0;
|
||||
dma_addr_t daddr;
|
||||
dma_addr_t save;
|
||||
|
||||
daddr = dma_map_single(&rdev->lldi.pdev->dev, data, len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(&rdev->lldi.pdev->dev, daddr))
|
||||
return -1;
|
||||
save = daddr;
|
||||
|
||||
while (remain > inline_threshold) {
|
||||
if (remain < T4_ULPTX_MAX_DMA) {
|
||||
if (remain & ~T4_ULPTX_MIN_IO)
|
||||
dmalen = remain & ~(T4_ULPTX_MIN_IO-1);
|
||||
else
|
||||
dmalen = remain;
|
||||
} else
|
||||
dmalen = T4_ULPTX_MAX_DMA;
|
||||
remain -= dmalen;
|
||||
ret = _c4iw_write_mem_dma_aligned(rdev, addr, dmalen, daddr,
|
||||
!remain);
|
||||
if (ret)
|
||||
goto out;
|
||||
addr += dmalen >> 5;
|
||||
data += dmalen;
|
||||
daddr += dmalen;
|
||||
}
|
||||
if (remain)
|
||||
ret = _c4iw_write_mem_inline(rdev, addr, remain, data);
|
||||
out:
|
||||
dma_unmap_single(&rdev->lldi.pdev->dev, save, len, DMA_TO_DEVICE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* write len bytes of data into addr (32B aligned address)
|
||||
* If data is NULL, clear len byte of memory to zero.
|
||||
*/
|
||||
static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
|
||||
void *data)
|
||||
{
|
||||
if (is_t5(rdev->lldi.adapter_type) && use_dsgl) {
|
||||
if (len > inline_threshold) {
|
||||
if (_c4iw_write_mem_dma(rdev, addr, len, data)) {
|
||||
printk_ratelimited(KERN_WARNING
|
||||
"%s: dma map"
|
||||
" failure (non fatal)\n",
|
||||
pci_name(rdev->lldi.pdev));
|
||||
return _c4iw_write_mem_inline(rdev, addr, len,
|
||||
data);
|
||||
} else
|
||||
return 0;
|
||||
} else
|
||||
return _c4iw_write_mem_inline(rdev, addr, len, data);
|
||||
} else
|
||||
return _c4iw_write_mem_inline(rdev, addr, len, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build and write a TPT entry.
|
||||
* IN: stag key, pdid, perm, bind_enabled, zbva, to, len, page_size,
|
||||
@ -760,19 +887,23 @@ struct ib_fast_reg_page_list *c4iw_alloc_fastreg_pbl(struct ib_device *device,
|
||||
struct c4iw_fr_page_list *c4pl;
|
||||
struct c4iw_dev *dev = to_c4iw_dev(device);
|
||||
dma_addr_t dma_addr;
|
||||
int size = sizeof *c4pl + page_list_len * sizeof(u64);
|
||||
int pll_len = roundup(page_list_len * sizeof(u64), 32);
|
||||
|
||||
c4pl = dma_alloc_coherent(&dev->rdev.lldi.pdev->dev, size,
|
||||
&dma_addr, GFP_KERNEL);
|
||||
c4pl = kmalloc(sizeof(*c4pl), GFP_KERNEL);
|
||||
if (!c4pl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
c4pl->ibpl.page_list = dma_alloc_coherent(&dev->rdev.lldi.pdev->dev,
|
||||
pll_len, &dma_addr,
|
||||
GFP_KERNEL);
|
||||
if (!c4pl->ibpl.page_list) {
|
||||
kfree(c4pl);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
dma_unmap_addr_set(c4pl, mapping, dma_addr);
|
||||
c4pl->dma_addr = dma_addr;
|
||||
c4pl->dev = dev;
|
||||
c4pl->size = size;
|
||||
c4pl->ibpl.page_list = (u64 *)(c4pl + 1);
|
||||
c4pl->ibpl.max_page_list_len = page_list_len;
|
||||
c4pl->ibpl.max_page_list_len = pll_len;
|
||||
|
||||
return &c4pl->ibpl;
|
||||
}
|
||||
@ -781,8 +912,10 @@ void c4iw_free_fastreg_pbl(struct ib_fast_reg_page_list *ibpl)
|
||||
{
|
||||
struct c4iw_fr_page_list *c4pl = to_c4iw_fr_page_list(ibpl);
|
||||
|
||||
dma_free_coherent(&c4pl->dev->rdev.lldi.pdev->dev, c4pl->size,
|
||||
c4pl, dma_unmap_addr(c4pl, mapping));
|
||||
dma_free_coherent(&c4pl->dev->rdev.lldi.pdev->dev,
|
||||
c4pl->ibpl.max_page_list_len,
|
||||
c4pl->ibpl.page_list, dma_unmap_addr(c4pl, mapping));
|
||||
kfree(c4pl);
|
||||
}
|
||||
|
||||
int c4iw_dereg_mr(struct ib_mr *ib_mr)
|
||||
|
@ -162,8 +162,14 @@ static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
|
||||
*/
|
||||
if (addr >= rdev->oc_mw_pa)
|
||||
vma->vm_page_prot = t4_pgprot_wc(vma->vm_page_prot);
|
||||
else
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
else {
|
||||
if (is_t5(rdev->lldi.adapter_type))
|
||||
vma->vm_page_prot =
|
||||
t4_pgprot_wc(vma->vm_page_prot);
|
||||
else
|
||||
vma->vm_page_prot =
|
||||
pgprot_noncached(vma->vm_page_prot);
|
||||
}
|
||||
ret = io_remap_pfn_range(vma, vma->vm_start,
|
||||
addr >> PAGE_SHIFT,
|
||||
len, vma->vm_page_prot);
|
||||
@ -263,7 +269,7 @@ static int c4iw_query_device(struct ib_device *ibdev,
|
||||
dev = to_c4iw_dev(ibdev);
|
||||
memset(props, 0, sizeof *props);
|
||||
memcpy(&props->sys_image_guid, dev->rdev.lldi.ports[0]->dev_addr, 6);
|
||||
props->hw_ver = dev->rdev.lldi.adapter_type;
|
||||
props->hw_ver = CHELSIO_CHIP_RELEASE(dev->rdev.lldi.adapter_type);
|
||||
props->fw_ver = dev->rdev.lldi.fw_vers;
|
||||
props->device_cap_flags = dev->device_cap_flags;
|
||||
props->page_size_cap = T4_PAGESIZE_MASK;
|
||||
@ -346,7 +352,8 @@ static ssize_t show_rev(struct device *dev, struct device_attribute *attr,
|
||||
struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev,
|
||||
ibdev.dev);
|
||||
PDBG("%s dev 0x%p\n", __func__, dev);
|
||||
return sprintf(buf, "%d\n", c4iw_dev->rdev.lldi.adapter_type);
|
||||
return sprintf(buf, "%d\n",
|
||||
CHELSIO_CHIP_RELEASE(c4iw_dev->rdev.lldi.adapter_type));
|
||||
}
|
||||
|
||||
static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
|
||||
|
@ -42,10 +42,21 @@ static int ocqp_support = 1;
|
||||
module_param(ocqp_support, int, 0644);
|
||||
MODULE_PARM_DESC(ocqp_support, "Support on-chip SQs (default=1)");
|
||||
|
||||
int db_fc_threshold = 2000;
|
||||
int db_fc_threshold = 1000;
|
||||
module_param(db_fc_threshold, int, 0644);
|
||||
MODULE_PARM_DESC(db_fc_threshold, "QP count/threshold that triggers automatic "
|
||||
"db flow control mode (default = 2000)");
|
||||
MODULE_PARM_DESC(db_fc_threshold,
|
||||
"QP count/threshold that triggers"
|
||||
" automatic db flow control mode (default = 1000)");
|
||||
|
||||
int db_coalescing_threshold;
|
||||
module_param(db_coalescing_threshold, int, 0644);
|
||||
MODULE_PARM_DESC(db_coalescing_threshold,
|
||||
"QP count/threshold that triggers"
|
||||
" disabling db coalescing (default = 0)");
|
||||
|
||||
static int max_fr_immd = T4_MAX_FR_IMMD;
|
||||
module_param(max_fr_immd, int, 0644);
|
||||
MODULE_PARM_DESC(max_fr_immd, "fastreg threshold for using DSGL instead of immedate");
|
||||
|
||||
static void set_state(struct c4iw_qp *qhp, enum c4iw_qp_state state)
|
||||
{
|
||||
@ -76,7 +87,7 @@ static void dealloc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
|
||||
|
||||
static int alloc_oc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
|
||||
{
|
||||
if (!ocqp_support || !t4_ocqp_supported())
|
||||
if (!ocqp_support || !ocqp_supported(&rdev->lldi))
|
||||
return -ENOSYS;
|
||||
sq->dma_addr = c4iw_ocqp_pool_alloc(rdev, sq->memsize);
|
||||
if (!sq->dma_addr)
|
||||
@ -129,7 +140,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
|
||||
int wr_len;
|
||||
struct c4iw_wr_wait wr_wait;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
int eqsize;
|
||||
|
||||
wq->sq.qid = c4iw_get_qpid(rdev, uctx);
|
||||
@ -169,17 +180,14 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
|
||||
}
|
||||
|
||||
if (user) {
|
||||
ret = alloc_oc_sq(rdev, &wq->sq);
|
||||
if (alloc_oc_sq(rdev, &wq->sq) && alloc_host_sq(rdev, &wq->sq))
|
||||
goto free_hwaddr;
|
||||
} else {
|
||||
ret = alloc_host_sq(rdev, &wq->sq);
|
||||
if (ret)
|
||||
goto free_hwaddr;
|
||||
}
|
||||
|
||||
ret = alloc_host_sq(rdev, &wq->sq);
|
||||
if (ret)
|
||||
goto free_sq;
|
||||
} else
|
||||
ret = alloc_host_sq(rdev, &wq->sq);
|
||||
if (ret)
|
||||
goto free_hwaddr;
|
||||
memset(wq->sq.queue, 0, wq->sq.memsize);
|
||||
dma_unmap_addr_set(&wq->sq, mapping, wq->sq.dma_addr);
|
||||
|
||||
@ -534,7 +542,7 @@ static int build_rdma_recv(struct c4iw_qp *qhp, union t4_recv_wr *wqe,
|
||||
}
|
||||
|
||||
static int build_fastreg(struct t4_sq *sq, union t4_wr *wqe,
|
||||
struct ib_send_wr *wr, u8 *len16)
|
||||
struct ib_send_wr *wr, u8 *len16, u8 t5dev)
|
||||
{
|
||||
|
||||
struct fw_ri_immd *imdp;
|
||||
@ -556,28 +564,51 @@ static int build_fastreg(struct t4_sq *sq, union t4_wr *wqe,
|
||||
wqe->fr.va_hi = cpu_to_be32(wr->wr.fast_reg.iova_start >> 32);
|
||||
wqe->fr.va_lo_fbo = cpu_to_be32(wr->wr.fast_reg.iova_start &
|
||||
0xffffffff);
|
||||
WARN_ON(pbllen > T4_MAX_FR_IMMD);
|
||||
imdp = (struct fw_ri_immd *)(&wqe->fr + 1);
|
||||
imdp->op = FW_RI_DATA_IMMD;
|
||||
imdp->r1 = 0;
|
||||
imdp->r2 = 0;
|
||||
imdp->immdlen = cpu_to_be32(pbllen);
|
||||
p = (__be64 *)(imdp + 1);
|
||||
rem = pbllen;
|
||||
for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {
|
||||
*p = cpu_to_be64((u64)wr->wr.fast_reg.page_list->page_list[i]);
|
||||
rem -= sizeof *p;
|
||||
if (++p == (__be64 *)&sq->queue[sq->size])
|
||||
p = (__be64 *)sq->queue;
|
||||
|
||||
if (t5dev && use_dsgl && (pbllen > max_fr_immd)) {
|
||||
struct c4iw_fr_page_list *c4pl =
|
||||
to_c4iw_fr_page_list(wr->wr.fast_reg.page_list);
|
||||
struct fw_ri_dsgl *sglp;
|
||||
|
||||
for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {
|
||||
wr->wr.fast_reg.page_list->page_list[i] = (__force u64)
|
||||
cpu_to_be64((u64)
|
||||
wr->wr.fast_reg.page_list->page_list[i]);
|
||||
}
|
||||
|
||||
sglp = (struct fw_ri_dsgl *)(&wqe->fr + 1);
|
||||
sglp->op = FW_RI_DATA_DSGL;
|
||||
sglp->r1 = 0;
|
||||
sglp->nsge = cpu_to_be16(1);
|
||||
sglp->addr0 = cpu_to_be64(c4pl->dma_addr);
|
||||
sglp->len0 = cpu_to_be32(pbllen);
|
||||
|
||||
*len16 = DIV_ROUND_UP(sizeof(wqe->fr) + sizeof(*sglp), 16);
|
||||
} else {
|
||||
imdp = (struct fw_ri_immd *)(&wqe->fr + 1);
|
||||
imdp->op = FW_RI_DATA_IMMD;
|
||||
imdp->r1 = 0;
|
||||
imdp->r2 = 0;
|
||||
imdp->immdlen = cpu_to_be32(pbllen);
|
||||
p = (__be64 *)(imdp + 1);
|
||||
rem = pbllen;
|
||||
for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {
|
||||
*p = cpu_to_be64(
|
||||
(u64)wr->wr.fast_reg.page_list->page_list[i]);
|
||||
rem -= sizeof(*p);
|
||||
if (++p == (__be64 *)&sq->queue[sq->size])
|
||||
p = (__be64 *)sq->queue;
|
||||
}
|
||||
BUG_ON(rem < 0);
|
||||
while (rem) {
|
||||
*p = 0;
|
||||
rem -= sizeof(*p);
|
||||
if (++p == (__be64 *)&sq->queue[sq->size])
|
||||
p = (__be64 *)sq->queue;
|
||||
}
|
||||
*len16 = DIV_ROUND_UP(sizeof(wqe->fr) + sizeof(*imdp)
|
||||
+ pbllen, 16);
|
||||
}
|
||||
BUG_ON(rem < 0);
|
||||
while (rem) {
|
||||
*p = 0;
|
||||
rem -= sizeof *p;
|
||||
if (++p == (__be64 *)&sq->queue[sq->size])
|
||||
p = (__be64 *)sq->queue;
|
||||
}
|
||||
*len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *imdp + pbllen, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -678,7 +709,10 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
|
||||
case IB_WR_FAST_REG_MR:
|
||||
fw_opcode = FW_RI_FR_NSMR_WR;
|
||||
swsqe->opcode = FW_RI_FAST_REGISTER;
|
||||
err = build_fastreg(&qhp->wq.sq, wqe, wr, &len16);
|
||||
err = build_fastreg(&qhp->wq.sq, wqe, wr, &len16,
|
||||
is_t5(
|
||||
qhp->rhp->rdev.lldi.adapter_type) ?
|
||||
1 : 0);
|
||||
break;
|
||||
case IB_WR_LOCAL_INV:
|
||||
if (wr->send_flags & IB_SEND_FENCE)
|
||||
@ -1450,6 +1484,9 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)
|
||||
rhp->db_state = NORMAL;
|
||||
idr_for_each(&rhp->qpidr, enable_qp_db, NULL);
|
||||
}
|
||||
if (db_coalescing_threshold >= 0)
|
||||
if (rhp->qpcnt <= db_coalescing_threshold)
|
||||
cxgb4_enable_db_coalescing(rhp->rdev.lldi.ports[0]);
|
||||
spin_unlock_irq(&rhp->lock);
|
||||
atomic_dec(&qhp->refcnt);
|
||||
wait_event(qhp->wait, !atomic_read(&qhp->refcnt));
|
||||
@ -1561,11 +1598,15 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
|
||||
spin_lock_irq(&rhp->lock);
|
||||
if (rhp->db_state != NORMAL)
|
||||
t4_disable_wq_db(&qhp->wq);
|
||||
if (++rhp->qpcnt > db_fc_threshold && rhp->db_state == NORMAL) {
|
||||
rhp->qpcnt++;
|
||||
if (rhp->qpcnt > db_fc_threshold && rhp->db_state == NORMAL) {
|
||||
rhp->rdev.stats.db_state_transitions++;
|
||||
rhp->db_state = FLOW_CONTROL;
|
||||
idr_for_each(&rhp->qpidr, disable_qp_db, NULL);
|
||||
}
|
||||
if (db_coalescing_threshold >= 0)
|
||||
if (rhp->qpcnt > db_coalescing_threshold)
|
||||
cxgb4_disable_db_coalescing(rhp->rdev.lldi.ports[0]);
|
||||
ret = insert_handle_nolock(rhp, &rhp->qpidr, qhp, qhp->wq.sq.qid);
|
||||
spin_unlock_irq(&rhp->lock);
|
||||
if (ret)
|
||||
|
@ -84,7 +84,7 @@ struct t4_status_page {
|
||||
sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge))
|
||||
#define T4_MAX_FR_IMMD ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_fr_nsmr_wr) - \
|
||||
sizeof(struct fw_ri_immd)) & ~31UL)
|
||||
#define T4_MAX_FR_DEPTH (T4_MAX_FR_IMMD / sizeof(u64))
|
||||
#define T4_MAX_FR_DEPTH (1024 / sizeof(u64))
|
||||
|
||||
#define T4_RQ_NUM_SLOTS 2
|
||||
#define T4_RQ_NUM_BYTES (T4_EQ_ENTRY_SIZE * T4_RQ_NUM_SLOTS)
|
||||
@ -280,15 +280,6 @@ static inline pgprot_t t4_pgprot_wc(pgprot_t prot)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int t4_ocqp_supported(void)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(CONFIG_PPC64)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
enum {
|
||||
T4_SQ_ONCHIP = (1<<0),
|
||||
};
|
||||
|
@ -228,7 +228,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
|
||||
vector = dev->eq_table[vector % ibdev->num_comp_vectors];
|
||||
|
||||
err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar,
|
||||
cq->db.dma, &cq->mcq, vector, 0);
|
||||
cq->db.dma, &cq->mcq, vector, 0, 0);
|
||||
if (err)
|
||||
goto err_dbmap;
|
||||
|
||||
|
@ -2948,7 +2948,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
|
||||
nes_debug(NES_DBG_CQ, "%s: Reporting stripped VLAN packet. Tag = 0x%04X\n",
|
||||
nesvnic->netdev->name, vlan_tag);
|
||||
|
||||
__vlan_hwaccel_put_tag(rx_skb, vlan_tag);
|
||||
__vlan_hwaccel_put_tag(rx_skb, htons(ETH_P_8021Q), vlan_tag);
|
||||
}
|
||||
if (nes_use_lro)
|
||||
lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL);
|
||||
|
@ -1599,7 +1599,7 @@ static void nes_vlan_mode(struct net_device *netdev, struct nes_device *nesdev,
|
||||
|
||||
/* Enable/Disable VLAN Stripping */
|
||||
u32temp = nes_read_indexed(nesdev, NES_IDX_PCIX_DIAG);
|
||||
if (features & NETIF_F_HW_VLAN_RX)
|
||||
if (features & NETIF_F_HW_VLAN_CTAG_RX)
|
||||
u32temp &= 0xfdffffff;
|
||||
else
|
||||
u32temp |= 0x02000000;
|
||||
@ -1614,10 +1614,10 @@ static netdev_features_t nes_fix_features(struct net_device *netdev, netdev_feat
|
||||
* Since there is no support for separate rx/tx vlan accel
|
||||
* enable/disable make sure tx flag is always in same state as rx.
|
||||
*/
|
||||
if (features & NETIF_F_HW_VLAN_RX)
|
||||
features |= NETIF_F_HW_VLAN_TX;
|
||||
if (features & NETIF_F_HW_VLAN_CTAG_RX)
|
||||
features |= NETIF_F_HW_VLAN_CTAG_TX;
|
||||
else
|
||||
features &= ~NETIF_F_HW_VLAN_TX;
|
||||
features &= ~NETIF_F_HW_VLAN_CTAG_TX;
|
||||
|
||||
return features;
|
||||
}
|
||||
@ -1628,7 +1628,7 @@ static int nes_set_features(struct net_device *netdev, netdev_features_t feature
|
||||
struct nes_device *nesdev = nesvnic->nesdev;
|
||||
u32 changed = netdev->features ^ features;
|
||||
|
||||
if (changed & NETIF_F_HW_VLAN_RX)
|
||||
if (changed & NETIF_F_HW_VLAN_CTAG_RX)
|
||||
nes_vlan_mode(netdev, nesdev, features);
|
||||
|
||||
return 0;
|
||||
@ -1706,11 +1706,11 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
|
||||
netdev->dev_addr[4] = (u8)(u64temp>>8);
|
||||
netdev->dev_addr[5] = (u8)u64temp;
|
||||
|
||||
netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX;
|
||||
netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX;
|
||||
if ((nesvnic->logical_port < 2) || (nesdev->nesadapter->hw_rev != NE020_REV))
|
||||
netdev->hw_features |= NETIF_F_TSO;
|
||||
|
||||
netdev->features = netdev->hw_features | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_TX;
|
||||
netdev->features = netdev->hw_features | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX;
|
||||
netdev->hw_features |= NETIF_F_LRO;
|
||||
|
||||
nes_debug(NES_DBG_INIT, "nesvnic = %p, reported features = 0x%lX, QPid = %d,"
|
||||
|
@ -730,7 +730,8 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
if ((header->proto != htons(ETH_P_IP)) &&
|
||||
(header->proto != htons(ETH_P_IPV6)) &&
|
||||
(header->proto != htons(ETH_P_ARP)) &&
|
||||
(header->proto != htons(ETH_P_RARP))) {
|
||||
(header->proto != htons(ETH_P_RARP)) &&
|
||||
(header->proto != htons(ETH_P_TIPC))) {
|
||||
/* ethertype not supported by IPoIB */
|
||||
++dev->stats.tx_dropped;
|
||||
dev_kfree_skb_any(skb);
|
||||
@ -751,6 +752,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
switch (header->proto) {
|
||||
case htons(ETH_P_IP):
|
||||
case htons(ETH_P_IPV6):
|
||||
case htons(ETH_P_TIPC):
|
||||
neigh = ipoib_neigh_get(dev, cb->hwaddr);
|
||||
if (unlikely(!neigh)) {
|
||||
neigh_add_path(skb, cb->hwaddr, dev);
|
||||
|
@ -469,8 +469,7 @@ static int capidrv_add_ack(struct capidrv_ncci *nccip,
|
||||
{
|
||||
struct ncci_datahandle_queue *n, **pp;
|
||||
|
||||
n = (struct ncci_datahandle_queue *)
|
||||
kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
|
||||
n = kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
|
||||
if (!n) {
|
||||
printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n");
|
||||
return -1;
|
||||
|
@ -441,8 +441,7 @@ static int isdn_divert_icall(isdn_ctrl *ic)
|
||||
|
||||
switch (dv->rule.action) {
|
||||
case DEFLECT_IGNORE:
|
||||
return (0);
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case DEFLECT_ALERT:
|
||||
case DEFLECT_PROCEED:
|
||||
@ -510,10 +509,9 @@ static int isdn_divert_icall(isdn_ctrl *ic)
|
||||
break;
|
||||
|
||||
default:
|
||||
return (0); /* ignore call */
|
||||
break;
|
||||
return 0; /* ignore call */
|
||||
} /* switch action */
|
||||
break;
|
||||
break; /* will break the 'for' looping */
|
||||
} /* scan_table */
|
||||
|
||||
if (cs) {
|
||||
|
@ -26,7 +26,7 @@ FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount)
|
||||
{
|
||||
int i;
|
||||
|
||||
fsm->jumpmatrix = (FSMFNPTR *)
|
||||
fsm->jumpmatrix =
|
||||
kzalloc(sizeof(FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
|
||||
if (!fsm->jumpmatrix)
|
||||
return -ENOMEM;
|
||||
|
@ -1479,7 +1479,7 @@ int setup_hfcsx(struct IsdnCard *card)
|
||||
release_region(cs->hw.hfcsx.base, 2);
|
||||
return (0);
|
||||
}
|
||||
if (!(cs->hw.hfcsx.extra = (void *)
|
||||
if (!(cs->hw.hfcsx.extra =
|
||||
kmalloc(sizeof(struct hfcsx_extra), GFP_ATOMIC))) {
|
||||
release_region(cs->hw.hfcsx.base, 2);
|
||||
printk(KERN_WARNING "HFC-SX: unable to allocate memory\n");
|
||||
|
@ -1385,7 +1385,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||
if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
}
|
||||
if (ntohs(eth->h_proto) >= 1536)
|
||||
if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
|
||||
return eth->h_proto;
|
||||
|
||||
rawp = skb->data;
|
||||
|
@ -578,6 +578,7 @@ data_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
lock_sock(sk);
|
||||
|
||||
*addr_len = sizeof(*maddr);
|
||||
maddr->family = AF_ISDN;
|
||||
maddr->dev = _pms(sk)->dev->id;
|
||||
maddr->channel = _pms(sk)->ch.nr;
|
||||
maddr->sapi = _pms(sk)->ch.addr & 0xff;
|
||||
|
@ -33,8 +33,8 @@ static unsigned long ram[] = {0, 0, 0, 0};
|
||||
static bool do_reset = 0;
|
||||
|
||||
module_param_array(io, int, NULL, 0);
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
module_param_array(ram, int, NULL, 0);
|
||||
module_param_array(irq, byte, NULL, 0);
|
||||
module_param_array(ram, long, NULL, 0);
|
||||
module_param(do_reset, bool, 0);
|
||||
|
||||
static int identify_board(unsigned long, unsigned int);
|
||||
|
@ -185,7 +185,7 @@ static __be16 dvb_net_eth_type_trans(struct sk_buff *skb,
|
||||
skb->pkt_type=PACKET_MULTICAST;
|
||||
}
|
||||
|
||||
if (ntohs(eth->h_proto) >= 1536)
|
||||
if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
|
||||
return eth->h_proto;
|
||||
|
||||
rawp = skb->data;
|
||||
@ -228,9 +228,9 @@ static int ule_test_sndu( struct dvb_net_priv *p )
|
||||
static int ule_bridged_sndu( struct dvb_net_priv *p )
|
||||
{
|
||||
struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr;
|
||||
if(ntohs(hdr->h_proto) < 1536) {
|
||||
if(ntohs(hdr->h_proto) < ETH_P_802_3_MIN) {
|
||||
int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data);
|
||||
/* A frame Type < 1536 for a bridged frame, introduces a LLC Length field. */
|
||||
/* A frame Type < ETH_P_802_3_MIN for a bridged frame, introduces a LLC Length field. */
|
||||
if(framelen != ntohs(hdr->h_proto)) {
|
||||
return -1;
|
||||
}
|
||||
@ -320,7 +320,7 @@ static int handle_ule_extensions( struct dvb_net_priv *p )
|
||||
(int) p->ule_sndu_type, l, total_ext_len);
|
||||
#endif
|
||||
|
||||
} while (p->ule_sndu_type < 1536);
|
||||
} while (p->ule_sndu_type < ETH_P_802_3_MIN);
|
||||
|
||||
return total_ext_len;
|
||||
}
|
||||
@ -712,7 +712,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
|
||||
}
|
||||
|
||||
/* Handle ULE Extension Headers. */
|
||||
if (priv->ule_sndu_type < 1536) {
|
||||
if (priv->ule_sndu_type < ETH_P_802_3_MIN) {
|
||||
/* There is an extension header. Handle it accordingly. */
|
||||
int l = handle_ule_extensions(priv);
|
||||
if (l < 0) {
|
||||
|
@ -151,6 +151,7 @@ config MACVTAP
|
||||
config VXLAN
|
||||
tristate "Virtual eXtensible Local Area Network (VXLAN)"
|
||||
depends on INET
|
||||
select NET_IP_TUNNEL
|
||||
---help---
|
||||
This allows one to create vxlan virtual interfaces that provide
|
||||
Layer 2 Networks over Layer 3 Networks. VXLAN is often used
|
||||
|
@ -106,20 +106,4 @@ config IPDDP_ENCAP
|
||||
IP packets inside AppleTalk frames; this is useful if your Linux box
|
||||
is stuck on an AppleTalk network (which hopefully contains a
|
||||
decapsulator somewhere). Please see
|
||||
<file:Documentation/networking/ipddp.txt> for more information. If
|
||||
you said Y to "AppleTalk-IP driver support" above and you say Y
|
||||
here, then you cannot say Y to "AppleTalk-IP to IP Decapsulation
|
||||
support", below.
|
||||
|
||||
config IPDDP_DECAP
|
||||
bool "Appletalk-IP to IP Decapsulation support"
|
||||
depends on IPDDP
|
||||
help
|
||||
If you say Y here, the AppleTalk-IP code will be able to decapsulate
|
||||
AppleTalk-IP frames to IP packets; this is useful if you want your
|
||||
Linux box to act as an Internet gateway for an AppleTalk network.
|
||||
Please see <file:Documentation/networking/ipddp.txt> for more
|
||||
information. If you said Y to "AppleTalk-IP driver support" above
|
||||
and you say Y here, then you cannot say Y to "IP to AppleTalk-IP
|
||||
Encapsulation support", above.
|
||||
|
||||
<file:Documentation/networking/ipddp.txt> for more information.
|
||||
|
@ -514,7 +514,7 @@ static void rlb_update_client(struct rlb_client_info *client_info)
|
||||
skb->dev = client_info->slave->dev;
|
||||
|
||||
if (client_info->tag) {
|
||||
skb = vlan_put_tag(skb, client_info->vlan_id);
|
||||
skb = vlan_put_tag(skb, htons(ETH_P_8021Q), client_info->vlan_id);
|
||||
if (!skb) {
|
||||
pr_err("%s: Error: failed to insert VLAN tag\n",
|
||||
client_info->slave->bond->dev->name);
|
||||
@ -1014,7 +1014,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
|
||||
continue;
|
||||
}
|
||||
|
||||
skb = vlan_put_tag(skb, vlan->vlan_id);
|
||||
skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vlan->vlan_id);
|
||||
if (!skb) {
|
||||
pr_err("%s: Error: failed to insert VLAN tag\n",
|
||||
bond->dev->name);
|
||||
|
@ -428,14 +428,15 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
|
||||
* @bond_dev: bonding net device that got called
|
||||
* @vid: vlan id being added
|
||||
*/
|
||||
static int bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
static int bond_vlan_rx_add_vid(struct net_device *bond_dev,
|
||||
__be16 proto, u16 vid)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct slave *slave, *stop_at;
|
||||
int i, res;
|
||||
|
||||
bond_for_each_slave(bond, slave, i) {
|
||||
res = vlan_vid_add(slave->dev, vid);
|
||||
res = vlan_vid_add(slave->dev, proto, vid);
|
||||
if (res)
|
||||
goto unwind;
|
||||
}
|
||||
@ -453,7 +454,7 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
/* unwind from head to the slave that failed */
|
||||
stop_at = slave;
|
||||
bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at)
|
||||
vlan_vid_del(slave->dev, vid);
|
||||
vlan_vid_del(slave->dev, proto, vid);
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -463,14 +464,15 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
* @bond_dev: bonding net device that got called
|
||||
* @vid: vlan id being removed
|
||||
*/
|
||||
static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
|
||||
static int bond_vlan_rx_kill_vid(struct net_device *bond_dev,
|
||||
__be16 proto, u16 vid)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct slave *slave;
|
||||
int i, res;
|
||||
|
||||
bond_for_each_slave(bond, slave, i)
|
||||
vlan_vid_del(slave->dev, vid);
|
||||
vlan_vid_del(slave->dev, proto, vid);
|
||||
|
||||
res = bond_del_vlan(bond, vid);
|
||||
if (res) {
|
||||
@ -488,7 +490,8 @@ static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *sla
|
||||
int res;
|
||||
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
res = vlan_vid_add(slave_dev, vlan->vlan_id);
|
||||
res = vlan_vid_add(slave_dev, htons(ETH_P_8021Q),
|
||||
vlan->vlan_id);
|
||||
if (res)
|
||||
pr_warning("%s: Failed to add vlan id %d to device %s\n",
|
||||
bond->dev->name, vlan->vlan_id,
|
||||
@ -504,7 +507,7 @@ static void bond_del_vlans_from_slave(struct bonding *bond,
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
if (!vlan->vlan_id)
|
||||
continue;
|
||||
vlan_vid_del(slave_dev, vlan->vlan_id);
|
||||
vlan_vid_del(slave_dev, htons(ETH_P_8021Q), vlan->vlan_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -779,7 +782,7 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
|
||||
|
||||
/* rejoin all groups on vlan devices */
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
vlan_dev = __vlan_find_dev_deep(bond_dev,
|
||||
vlan_dev = __vlan_find_dev_deep(bond_dev, htons(ETH_P_8021Q),
|
||||
vlan->vlan_id);
|
||||
if (vlan_dev)
|
||||
__bond_resend_igmp_join_requests(vlan_dev);
|
||||
@ -796,9 +799,8 @@ static void bond_resend_igmp_join_requests_delayed(struct work_struct *work)
|
||||
{
|
||||
struct bonding *bond = container_of(work, struct bonding,
|
||||
mcast_work.work);
|
||||
rcu_read_lock();
|
||||
|
||||
bond_resend_igmp_join_requests(bond);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1915,14 +1917,16 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
||||
bond_detach_slave(bond, new_slave);
|
||||
if (bond->primary_slave == new_slave)
|
||||
bond->primary_slave = NULL;
|
||||
write_unlock_bh(&bond->lock);
|
||||
if (bond->curr_active_slave == new_slave) {
|
||||
bond_change_active_slave(bond, NULL);
|
||||
write_unlock_bh(&bond->lock);
|
||||
read_lock(&bond->lock);
|
||||
write_lock_bh(&bond->curr_slave_lock);
|
||||
bond_change_active_slave(bond, NULL);
|
||||
bond_select_active_slave(bond);
|
||||
write_unlock_bh(&bond->curr_slave_lock);
|
||||
read_unlock(&bond->lock);
|
||||
} else {
|
||||
write_unlock_bh(&bond->lock);
|
||||
}
|
||||
slave_disable_netpoll(new_slave);
|
||||
|
||||
@ -2532,7 +2536,8 @@ static int bond_has_this_ip(struct bonding *bond, __be32 ip)
|
||||
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
rcu_read_lock();
|
||||
vlan_dev = __vlan_find_dev_deep(bond->dev, vlan->vlan_id);
|
||||
vlan_dev = __vlan_find_dev_deep(bond->dev, htons(ETH_P_8021Q),
|
||||
vlan->vlan_id);
|
||||
rcu_read_unlock();
|
||||
if (vlan_dev && ip == bond_confirm_addr(vlan_dev, 0, ip))
|
||||
return 1;
|
||||
@ -2561,7 +2566,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_
|
||||
return;
|
||||
}
|
||||
if (vlan_id) {
|
||||
skb = vlan_put_tag(skb, vlan_id);
|
||||
skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
|
||||
if (!skb) {
|
||||
pr_err("failed to insert VLAN tag\n");
|
||||
return;
|
||||
@ -2623,6 +2628,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
|
||||
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
||||
rcu_read_lock();
|
||||
vlan_dev = __vlan_find_dev_deep(bond->dev,
|
||||
htons(ETH_P_8021Q),
|
||||
vlan->vlan_id);
|
||||
rcu_read_unlock();
|
||||
if (vlan_dev == rt->dst.dev) {
|
||||
@ -4258,6 +4264,37 @@ void bond_set_mode_ops(struct bonding *bond, int mode)
|
||||
}
|
||||
}
|
||||
|
||||
static int bond_ethtool_get_settings(struct net_device *bond_dev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct slave *slave;
|
||||
int i;
|
||||
unsigned long speed = 0;
|
||||
|
||||
ecmd->duplex = DUPLEX_UNKNOWN;
|
||||
ecmd->port = PORT_OTHER;
|
||||
|
||||
/* Since SLAVE_IS_OK returns false for all inactive or down slaves, we
|
||||
* do not need to check mode. Though link speed might not represent
|
||||
* the true receive or transmit bandwidth (not all modes are symmetric)
|
||||
* this is an accurate maximum.
|
||||
*/
|
||||
read_lock(&bond->lock);
|
||||
bond_for_each_slave(bond, slave, i) {
|
||||
if (SLAVE_IS_OK(slave)) {
|
||||
if (slave->speed != SPEED_UNKNOWN)
|
||||
speed += slave->speed;
|
||||
if (ecmd->duplex == DUPLEX_UNKNOWN &&
|
||||
slave->duplex != DUPLEX_UNKNOWN)
|
||||
ecmd->duplex = slave->duplex;
|
||||
}
|
||||
}
|
||||
ethtool_cmd_speed_set(ecmd, speed ? : SPEED_UNKNOWN);
|
||||
read_unlock(&bond->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
|
||||
struct ethtool_drvinfo *drvinfo)
|
||||
{
|
||||
@ -4269,6 +4306,7 @@ static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
|
||||
|
||||
static const struct ethtool_ops bond_ethtool_ops = {
|
||||
.get_drvinfo = bond_ethtool_get_drvinfo,
|
||||
.get_settings = bond_ethtool_get_settings,
|
||||
.get_link = ethtool_op_get_link,
|
||||
};
|
||||
|
||||
@ -4359,9 +4397,9 @@ static void bond_setup(struct net_device *bond_dev)
|
||||
*/
|
||||
|
||||
bond_dev->hw_features = BOND_VLAN_FEATURES |
|
||||
NETIF_F_HW_VLAN_TX |
|
||||
NETIF_F_HW_VLAN_RX |
|
||||
NETIF_F_HW_VLAN_FILTER;
|
||||
NETIF_F_HW_VLAN_CTAG_TX |
|
||||
NETIF_F_HW_VLAN_CTAG_RX |
|
||||
NETIF_F_HW_VLAN_CTAG_FILTER;
|
||||
|
||||
bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM);
|
||||
bond_dev->features |= bond_dev->hw_features;
|
||||
|
@ -32,13 +32,6 @@ config CAIF_SPI_SYNC
|
||||
help to synchronize to the next transfer in case of over or under-runs.
|
||||
This option also needs to be enabled on the modem.
|
||||
|
||||
config CAIF_SHM
|
||||
tristate "CAIF shared memory protocol driver"
|
||||
depends on CAIF && U5500_MBOX
|
||||
default n
|
||||
---help---
|
||||
The CAIF shared memory protocol driver for the STE UX5500 platform.
|
||||
|
||||
config CAIF_HSI
|
||||
tristate "CAIF HSI transport driver"
|
||||
depends on CAIF
|
||||
|
@ -7,9 +7,5 @@ obj-$(CONFIG_CAIF_TTY) += caif_serial.o
|
||||
cfspi_slave-objs := caif_spi.o caif_spi_slave.o
|
||||
obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o
|
||||
|
||||
# Shared memory
|
||||
caif_shm-objs := caif_shmcore.o caif_shm_u5500.o
|
||||
obj-$(CONFIG_CAIF_SHM) += caif_shm.o
|
||||
|
||||
# HSI interface
|
||||
obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
|
||||
|
@ -1,8 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson AB 2010
|
||||
* Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
|
||||
* Author: Daniel Martensson / daniel.martensson@stericsson.com
|
||||
* Dmitry.Tarnyagin / dmitry.tarnyagin@stericsson.com
|
||||
* Author: Daniel Martensson
|
||||
* Dmitry.Tarnyagin / dmitry.tarnyagin@lockless.no
|
||||
* License terms: GNU General Public License (GPL) version 2.
|
||||
*/
|
||||
|
||||
@ -25,7 +24,7 @@
|
||||
#include <net/caif/caif_hsi.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Daniel Martensson<daniel.martensson@stericsson.com>");
|
||||
MODULE_AUTHOR("Daniel Martensson");
|
||||
MODULE_DESCRIPTION("CAIF HSI driver");
|
||||
|
||||
/* Returns the number of padding bytes for alignment. */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson AB 2010
|
||||
* Author: Sjur Brendeland / sjur.brandeland@stericsson.com
|
||||
* Author: Sjur Brendeland
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Sjur Brendeland<sjur.brandeland@stericsson.com>");
|
||||
MODULE_AUTHOR("Sjur Brendeland");
|
||||
MODULE_DESCRIPTION("CAIF serial device TTY line discipline");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_LDISC(N_CAIF);
|
||||
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson AB 2010
|
||||
* Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
|
||||
* Author: Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <mach/mbox-db5500.h>
|
||||
#include <net/caif/caif_shm.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("CAIF Shared Memory protocol driver");
|
||||
|
||||
#define MAX_SHM_INSTANCES 1
|
||||
|
||||
enum {
|
||||
MBX_ACC0,
|
||||
MBX_ACC1,
|
||||
MBX_DSP
|
||||
};
|
||||
|
||||
static struct shmdev_layer shmdev_lyr[MAX_SHM_INSTANCES];
|
||||
|
||||
static unsigned int shm_start;
|
||||
static unsigned int shm_size;
|
||||
|
||||
module_param(shm_size, uint , 0440);
|
||||
MODULE_PARM_DESC(shm_total_size, "Start of SHM shared memory");
|
||||
|
||||
module_param(shm_start, uint , 0440);
|
||||
MODULE_PARM_DESC(shm_total_start, "Total Size of SHM shared memory");
|
||||
|
||||
static int shmdev_send_msg(u32 dev_id, u32 mbx_msg)
|
||||
{
|
||||
/* Always block until msg is written successfully */
|
||||
mbox_send(shmdev_lyr[dev_id].hmbx, mbx_msg, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shmdev_mbx_setup(void *pshmdrv_cb, struct shmdev_layer *pshm_dev,
|
||||
void *pshm_drv)
|
||||
{
|
||||
/*
|
||||
* For UX5500, we have only 1 SHM instance which uses MBX0
|
||||
* for communication with the peer modem
|
||||
*/
|
||||
pshm_dev->hmbx = mbox_setup(MBX_ACC0, pshmdrv_cb, pshm_drv);
|
||||
|
||||
if (!pshm_dev->hmbx)
|
||||
return -ENODEV;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init caif_shmdev_init(void)
|
||||
{
|
||||
int i, result;
|
||||
|
||||
/* Loop is currently overkill, there is only one instance */
|
||||
for (i = 0; i < MAX_SHM_INSTANCES; i++) {
|
||||
|
||||
shmdev_lyr[i].shm_base_addr = shm_start;
|
||||
shmdev_lyr[i].shm_total_sz = shm_size;
|
||||
|
||||
if (((char *)shmdev_lyr[i].shm_base_addr == NULL)
|
||||
|| (shmdev_lyr[i].shm_total_sz <= 0)) {
|
||||
pr_warn("ERROR,"
|
||||
"Shared memory Address and/or Size incorrect"
|
||||
", Bailing out ...\n");
|
||||
result = -EINVAL;
|
||||
goto clean;
|
||||
}
|
||||
|
||||
pr_info("SHM AREA (instance %d) STARTS"
|
||||
" AT %p\n", i, (char *)shmdev_lyr[i].shm_base_addr);
|
||||
|
||||
shmdev_lyr[i].shm_id = i;
|
||||
shmdev_lyr[i].pshmdev_mbxsend = shmdev_send_msg;
|
||||
shmdev_lyr[i].pshmdev_mbxsetup = shmdev_mbx_setup;
|
||||
|
||||
/*
|
||||
* Finally, CAIF core module is called with details in place:
|
||||
* 1. SHM base address
|
||||
* 2. SHM size
|
||||
* 3. MBX handle
|
||||
*/
|
||||
result = caif_shmcore_probe(&shmdev_lyr[i]);
|
||||
if (result) {
|
||||
pr_warn("ERROR[%d],"
|
||||
"Could not probe SHM core (instance %d)"
|
||||
" Bailing out ...\n", result, i);
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clean:
|
||||
/*
|
||||
* For now, we assume that even if one instance of SHM fails, we bail
|
||||
* out of the driver support completely. For this, we need to release
|
||||
* any memory allocated and unregister any instance of SHM net device.
|
||||
*/
|
||||
for (i = 0; i < MAX_SHM_INSTANCES; i++) {
|
||||
if (shmdev_lyr[i].pshm_netdev)
|
||||
unregister_netdev(shmdev_lyr[i].pshm_netdev);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit caif_shmdev_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_SHM_INSTANCES; i++) {
|
||||
caif_shmcore_remove(shmdev_lyr[i].pshm_netdev);
|
||||
kfree((void *)shmdev_lyr[i].shm_base_addr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module_init(caif_shmdev_init);
|
||||
module_exit(caif_shmdev_exit);
|
@ -1,747 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson AB 2010
|
||||
* Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
|
||||
* Authors: Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com,
|
||||
* Daniel Martensson / daniel.martensson@stericsson.com
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <net/caif/caif_device.h>
|
||||
#include <net/caif/caif_shm.h>
|
||||
|
||||
#define NR_TX_BUF 6
|
||||
#define NR_RX_BUF 6
|
||||
#define TX_BUF_SZ 0x2000
|
||||
#define RX_BUF_SZ 0x2000
|
||||
|
||||
#define CAIF_NEEDED_HEADROOM 32
|
||||
|
||||
#define CAIF_FLOW_ON 1
|
||||
#define CAIF_FLOW_OFF 0
|
||||
|
||||
#define LOW_WATERMARK 3
|
||||
#define HIGH_WATERMARK 4
|
||||
|
||||
/* Maximum number of CAIF buffers per shared memory buffer. */
|
||||
#define SHM_MAX_FRMS_PER_BUF 10
|
||||
|
||||
/*
|
||||
* Size in bytes of the descriptor area
|
||||
* (With end of descriptor signalling)
|
||||
*/
|
||||
#define SHM_CAIF_DESC_SIZE ((SHM_MAX_FRMS_PER_BUF + 1) * \
|
||||
sizeof(struct shm_pck_desc))
|
||||
|
||||
/*
|
||||
* Offset to the first CAIF frame within a shared memory buffer.
|
||||
* Aligned on 32 bytes.
|
||||
*/
|
||||
#define SHM_CAIF_FRM_OFS (SHM_CAIF_DESC_SIZE + (SHM_CAIF_DESC_SIZE % 32))
|
||||
|
||||
/* Number of bytes for CAIF shared memory header. */
|
||||
#define SHM_HDR_LEN 1
|
||||
|
||||
/* Number of padding bytes for the complete CAIF frame. */
|
||||
#define SHM_FRM_PAD_LEN 4
|
||||
|
||||
#define CAIF_MAX_MTU 4096
|
||||
|
||||
#define SHM_SET_FULL(x) (((x+1) & 0x0F) << 0)
|
||||
#define SHM_GET_FULL(x) (((x >> 0) & 0x0F) - 1)
|
||||
|
||||
#define SHM_SET_EMPTY(x) (((x+1) & 0x0F) << 4)
|
||||
#define SHM_GET_EMPTY(x) (((x >> 4) & 0x0F) - 1)
|
||||
|
||||
#define SHM_FULL_MASK (0x0F << 0)
|
||||
#define SHM_EMPTY_MASK (0x0F << 4)
|
||||
|
||||
struct shm_pck_desc {
|
||||
/*
|
||||
* Offset from start of shared memory area to start of
|
||||
* shared memory CAIF frame.
|
||||
*/
|
||||
u32 frm_ofs;
|
||||
u32 frm_len;
|
||||
};
|
||||
|
||||
struct buf_list {
|
||||
unsigned char *desc_vptr;
|
||||
u32 phy_addr;
|
||||
u32 index;
|
||||
u32 len;
|
||||
u32 frames;
|
||||
u32 frm_ofs;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct shm_caif_frm {
|
||||
/* Number of bytes of padding before the CAIF frame. */
|
||||
u8 hdr_ofs;
|
||||
};
|
||||
|
||||
struct shmdrv_layer {
|
||||
/* caif_dev_common must always be first in the structure*/
|
||||
struct caif_dev_common cfdev;
|
||||
|
||||
u32 shm_tx_addr;
|
||||
u32 shm_rx_addr;
|
||||
u32 shm_base_addr;
|
||||
u32 tx_empty_available;
|
||||
spinlock_t lock;
|
||||
|
||||
struct list_head tx_empty_list;
|
||||
struct list_head tx_pend_list;
|
||||
struct list_head tx_full_list;
|
||||
struct list_head rx_empty_list;
|
||||
struct list_head rx_pend_list;
|
||||
struct list_head rx_full_list;
|
||||
|
||||
struct workqueue_struct *pshm_tx_workqueue;
|
||||
struct workqueue_struct *pshm_rx_workqueue;
|
||||
|
||||
struct work_struct shm_tx_work;
|
||||
struct work_struct shm_rx_work;
|
||||
|
||||
struct sk_buff_head sk_qhead;
|
||||
struct shmdev_layer *pshm_dev;
|
||||
};
|
||||
|
||||
static int shm_netdev_open(struct net_device *shm_netdev)
|
||||
{
|
||||
netif_wake_queue(shm_netdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shm_netdev_close(struct net_device *shm_netdev)
|
||||
{
|
||||
netif_stop_queue(shm_netdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv)
|
||||
{
|
||||
struct buf_list *pbuf;
|
||||
struct shmdrv_layer *pshm_drv;
|
||||
struct list_head *pos;
|
||||
u32 avail_emptybuff = 0;
|
||||
unsigned long flags = 0;
|
||||
|
||||
pshm_drv = priv;
|
||||
|
||||
/* Check for received buffers. */
|
||||
if (mbx_msg & SHM_FULL_MASK) {
|
||||
int idx;
|
||||
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
|
||||
/* Check whether we have any outstanding buffers. */
|
||||
if (list_empty(&pshm_drv->rx_empty_list)) {
|
||||
|
||||
/* Release spin lock. */
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* We print even in IRQ context... */
|
||||
pr_warn("No empty Rx buffers to fill: "
|
||||
"mbx_msg:%x\n", mbx_msg);
|
||||
|
||||
/* Bail out. */
|
||||
goto err_sync;
|
||||
}
|
||||
|
||||
pbuf =
|
||||
list_entry(pshm_drv->rx_empty_list.next,
|
||||
struct buf_list, list);
|
||||
idx = pbuf->index;
|
||||
|
||||
/* Check buffer synchronization. */
|
||||
if (idx != SHM_GET_FULL(mbx_msg)) {
|
||||
|
||||
/* We print even in IRQ context... */
|
||||
pr_warn(
|
||||
"phyif_shm_mbx_msg_cb: RX full out of sync:"
|
||||
" idx:%d, msg:%x SHM_GET_FULL(mbx_msg):%x\n",
|
||||
idx, mbx_msg, SHM_GET_FULL(mbx_msg));
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* Bail out. */
|
||||
goto err_sync;
|
||||
}
|
||||
|
||||
list_del_init(&pbuf->list);
|
||||
list_add_tail(&pbuf->list, &pshm_drv->rx_full_list);
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* Schedule RX work queue. */
|
||||
if (!work_pending(&pshm_drv->shm_rx_work))
|
||||
queue_work(pshm_drv->pshm_rx_workqueue,
|
||||
&pshm_drv->shm_rx_work);
|
||||
}
|
||||
|
||||
/* Check for emptied buffers. */
|
||||
if (mbx_msg & SHM_EMPTY_MASK) {
|
||||
int idx;
|
||||
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
|
||||
/* Check whether we have any outstanding buffers. */
|
||||
if (list_empty(&pshm_drv->tx_full_list)) {
|
||||
|
||||
/* We print even in IRQ context... */
|
||||
pr_warn("No TX to empty: msg:%x\n", mbx_msg);
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* Bail out. */
|
||||
goto err_sync;
|
||||
}
|
||||
|
||||
pbuf =
|
||||
list_entry(pshm_drv->tx_full_list.next,
|
||||
struct buf_list, list);
|
||||
idx = pbuf->index;
|
||||
|
||||
/* Check buffer synchronization. */
|
||||
if (idx != SHM_GET_EMPTY(mbx_msg)) {
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* We print even in IRQ context... */
|
||||
pr_warn("TX empty "
|
||||
"out of sync:idx:%d, msg:%x\n", idx, mbx_msg);
|
||||
|
||||
/* Bail out. */
|
||||
goto err_sync;
|
||||
}
|
||||
list_del_init(&pbuf->list);
|
||||
|
||||
/* Reset buffer parameters. */
|
||||
pbuf->frames = 0;
|
||||
pbuf->frm_ofs = SHM_CAIF_FRM_OFS;
|
||||
|
||||
list_add_tail(&pbuf->list, &pshm_drv->tx_empty_list);
|
||||
|
||||
/* Check the available no. of buffers in the empty list */
|
||||
list_for_each(pos, &pshm_drv->tx_empty_list)
|
||||
avail_emptybuff++;
|
||||
|
||||
/* Check whether we have to wake up the transmitter. */
|
||||
if ((avail_emptybuff > HIGH_WATERMARK) &&
|
||||
(!pshm_drv->tx_empty_available)) {
|
||||
pshm_drv->tx_empty_available = 1;
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
pshm_drv->cfdev.flowctrl
|
||||
(pshm_drv->pshm_dev->pshm_netdev,
|
||||
CAIF_FLOW_ON);
|
||||
|
||||
|
||||
/* Schedule the work queue. if required */
|
||||
if (!work_pending(&pshm_drv->shm_tx_work))
|
||||
queue_work(pshm_drv->pshm_tx_workqueue,
|
||||
&pshm_drv->shm_tx_work);
|
||||
} else
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_sync:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void shm_rx_work_func(struct work_struct *rx_work)
|
||||
{
|
||||
struct shmdrv_layer *pshm_drv;
|
||||
struct buf_list *pbuf;
|
||||
unsigned long flags = 0;
|
||||
struct sk_buff *skb;
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
pshm_drv = container_of(rx_work, struct shmdrv_layer, shm_rx_work);
|
||||
|
||||
while (1) {
|
||||
|
||||
struct shm_pck_desc *pck_desc;
|
||||
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
|
||||
/* Check for received buffers. */
|
||||
if (list_empty(&pshm_drv->rx_full_list)) {
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf =
|
||||
list_entry(pshm_drv->rx_full_list.next, struct buf_list,
|
||||
list);
|
||||
list_del_init(&pbuf->list);
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
/* Retrieve pointer to start of the packet descriptor area. */
|
||||
pck_desc = (struct shm_pck_desc *) pbuf->desc_vptr;
|
||||
|
||||
/*
|
||||
* Check whether descriptor contains a CAIF shared memory
|
||||
* frame.
|
||||
*/
|
||||
while (pck_desc->frm_ofs) {
|
||||
unsigned int frm_buf_ofs;
|
||||
unsigned int frm_pck_ofs;
|
||||
unsigned int frm_pck_len;
|
||||
/*
|
||||
* Check whether offset is within buffer limits
|
||||
* (lower).
|
||||
*/
|
||||
if (pck_desc->frm_ofs <
|
||||
(pbuf->phy_addr - pshm_drv->shm_base_addr))
|
||||
break;
|
||||
/*
|
||||
* Check whether offset is within buffer limits
|
||||
* (higher).
|
||||
*/
|
||||
if (pck_desc->frm_ofs >
|
||||
((pbuf->phy_addr - pshm_drv->shm_base_addr) +
|
||||
pbuf->len))
|
||||
break;
|
||||
|
||||
/* Calculate offset from start of buffer. */
|
||||
frm_buf_ofs =
|
||||
pck_desc->frm_ofs - (pbuf->phy_addr -
|
||||
pshm_drv->shm_base_addr);
|
||||
|
||||
/*
|
||||
* Calculate offset and length of CAIF packet while
|
||||
* taking care of the shared memory header.
|
||||
*/
|
||||
frm_pck_ofs =
|
||||
frm_buf_ofs + SHM_HDR_LEN +
|
||||
(*(pbuf->desc_vptr + frm_buf_ofs));
|
||||
frm_pck_len =
|
||||
(pck_desc->frm_len - SHM_HDR_LEN -
|
||||
(*(pbuf->desc_vptr + frm_buf_ofs)));
|
||||
|
||||
/* Check whether CAIF packet is within buffer limits */
|
||||
if ((frm_pck_ofs + pck_desc->frm_len) > pbuf->len)
|
||||
break;
|
||||
|
||||
/* Get a suitable CAIF packet and copy in data. */
|
||||
skb = netdev_alloc_skb(pshm_drv->pshm_dev->pshm_netdev,
|
||||
frm_pck_len + 1);
|
||||
|
||||
if (skb == NULL) {
|
||||
pr_info("OOM: Try next frame in descriptor\n");
|
||||
break;
|
||||
}
|
||||
|
||||
p = skb_put(skb, frm_pck_len);
|
||||
memcpy(p, pbuf->desc_vptr + frm_pck_ofs, frm_pck_len);
|
||||
|
||||
skb->protocol = htons(ETH_P_CAIF);
|
||||
skb_reset_mac_header(skb);
|
||||
skb->dev = pshm_drv->pshm_dev->pshm_netdev;
|
||||
|
||||
/* Push received packet up the stack. */
|
||||
ret = netif_rx_ni(skb);
|
||||
|
||||
if (!ret) {
|
||||
pshm_drv->pshm_dev->pshm_netdev->stats.
|
||||
rx_packets++;
|
||||
pshm_drv->pshm_dev->pshm_netdev->stats.
|
||||
rx_bytes += pck_desc->frm_len;
|
||||
} else
|
||||
++pshm_drv->pshm_dev->pshm_netdev->stats.
|
||||
rx_dropped;
|
||||
/* Move to next packet descriptor. */
|
||||
pck_desc++;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
list_add_tail(&pbuf->list, &pshm_drv->rx_pend_list);
|
||||
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
}
|
||||
|
||||
/* Schedule the work queue. if required */
|
||||
if (!work_pending(&pshm_drv->shm_tx_work))
|
||||
queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work);
|
||||
|
||||
}
|
||||
|
||||
static void shm_tx_work_func(struct work_struct *tx_work)
|
||||
{
|
||||
u32 mbox_msg;
|
||||
unsigned int frmlen, avail_emptybuff, append = 0;
|
||||
unsigned long flags = 0;
|
||||
struct buf_list *pbuf = NULL;
|
||||
struct shmdrv_layer *pshm_drv;
|
||||
struct shm_caif_frm *frm;
|
||||
struct sk_buff *skb;
|
||||
struct shm_pck_desc *pck_desc;
|
||||
struct list_head *pos;
|
||||
|
||||
pshm_drv = container_of(tx_work, struct shmdrv_layer, shm_tx_work);
|
||||
|
||||
do {
|
||||
/* Initialize mailbox message. */
|
||||
mbox_msg = 0x00;
|
||||
avail_emptybuff = 0;
|
||||
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
|
||||
/* Check for pending receive buffers. */
|
||||
if (!list_empty(&pshm_drv->rx_pend_list)) {
|
||||
|
||||
pbuf = list_entry(pshm_drv->rx_pend_list.next,
|
||||
struct buf_list, list);
|
||||
|
||||
list_del_init(&pbuf->list);
|
||||
list_add_tail(&pbuf->list, &pshm_drv->rx_empty_list);
|
||||
/*
|
||||
* Value index is never changed,
|
||||
* so read access should be safe.
|
||||
*/
|
||||
mbox_msg |= SHM_SET_EMPTY(pbuf->index);
|
||||
}
|
||||
|
||||
skb = skb_peek(&pshm_drv->sk_qhead);
|
||||
|
||||
if (skb == NULL)
|
||||
goto send_msg;
|
||||
/* Check the available no. of buffers in the empty list */
|
||||
list_for_each(pos, &pshm_drv->tx_empty_list)
|
||||
avail_emptybuff++;
|
||||
|
||||
if ((avail_emptybuff < LOW_WATERMARK) &&
|
||||
pshm_drv->tx_empty_available) {
|
||||
/* Update blocking condition. */
|
||||
pshm_drv->tx_empty_available = 0;
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
pshm_drv->cfdev.flowctrl
|
||||
(pshm_drv->pshm_dev->pshm_netdev,
|
||||
CAIF_FLOW_OFF);
|
||||
spin_lock_irqsave(&pshm_drv->lock, flags);
|
||||
}
|
||||
/*
|
||||
* We simply return back to the caller if we do not have space
|
||||
* either in Tx pending list or Tx empty list. In this case,
|
||||
* we hold the received skb in the skb list, waiting to
|
||||
* be transmitted once Tx buffers become available
|
||||
*/
|
||||
if (list_empty(&pshm_drv->tx_empty_list))
|
||||
goto send_msg;
|
||||
|
||||
/* Get the first free Tx buffer. */
|
||||
pbuf = list_entry(pshm_drv->tx_empty_list.next,
|
||||
struct buf_list, list);
|
||||
do {
|
||||
if (append) {
|
||||
skb = skb_peek(&pshm_drv->sk_qhead);
|
||||
if (skb == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
frm = (struct shm_caif_frm *)
|
||||
(pbuf->desc_vptr + pbuf->frm_ofs);
|
||||
|
||||
frm->hdr_ofs = 0;
|
||||
frmlen = 0;
|
||||
frmlen += SHM_HDR_LEN + frm->hdr_ofs + skb->len;
|
||||
|
||||
/* Add tail padding if needed. */
|
||||
if (frmlen % SHM_FRM_PAD_LEN)
|
||||
frmlen += SHM_FRM_PAD_LEN -
|
||||
(frmlen % SHM_FRM_PAD_LEN);
|
||||
|
||||
/*
|
||||
* Verify that packet, header and additional padding
|
||||
* can fit within the buffer frame area.
|
||||
*/
|
||||
if (frmlen >= (pbuf->len - pbuf->frm_ofs))
|
||||
break;
|
||||
|
||||
if (!append) {
|
||||
list_del_init(&pbuf->list);
|
||||
append = 1;
|
||||
}
|
||||
|
||||
skb = skb_dequeue(&pshm_drv->sk_qhead);
|
||||
if (skb == NULL)
|
||||
break;
|
||||
/* Copy in CAIF frame. */
|
||||
skb_copy_bits(skb, 0, pbuf->desc_vptr +
|
||||
pbuf->frm_ofs + SHM_HDR_LEN +
|
||||
frm->hdr_ofs, skb->len);
|
||||
|
||||
pshm_drv->pshm_dev->pshm_netdev->stats.tx_packets++;
|
||||
pshm_drv->pshm_dev->pshm_netdev->stats.tx_bytes +=
|
||||
frmlen;
|
||||
dev_kfree_skb_irq(skb);
|
||||
|
||||
/* Fill in the shared memory packet descriptor area. */
|
||||
pck_desc = (struct shm_pck_desc *) (pbuf->desc_vptr);
|
||||
/* Forward to current frame. */
|
||||
pck_desc += pbuf->frames;
|
||||
pck_desc->frm_ofs = (pbuf->phy_addr -
|
||||
pshm_drv->shm_base_addr) +
|
||||
pbuf->frm_ofs;
|
||||
pck_desc->frm_len = frmlen;
|
||||
/* Terminate packet descriptor area. */
|
||||
pck_desc++;
|
||||
pck_desc->frm_ofs = 0;
|
||||
/* Update buffer parameters. */
|
||||
pbuf->frames++;
|
||||
pbuf->frm_ofs += frmlen + (frmlen % 32);
|
||||
|
||||
} while (pbuf->frames < SHM_MAX_FRMS_PER_BUF);
|
||||
|
||||
/* Assign buffer as full. */
|
||||
list_add_tail(&pbuf->list, &pshm_drv->tx_full_list);
|
||||
append = 0;
|
||||
mbox_msg |= SHM_SET_FULL(pbuf->index);
|
||||
send_msg:
|
||||
spin_unlock_irqrestore(&pshm_drv->lock, flags);
|
||||
|
||||
if (mbox_msg)
|
||||
pshm_drv->pshm_dev->pshmdev_mbxsend
|
||||
(pshm_drv->pshm_dev->shm_id, mbox_msg);
|
||||
} while (mbox_msg);
|
||||
}
|
||||
|
||||
static int shm_netdev_tx(struct sk_buff *skb, struct net_device *shm_netdev)
|
||||
{
|
||||
struct shmdrv_layer *pshm_drv;
|
||||
|
||||
pshm_drv = netdev_priv(shm_netdev);
|
||||
|
||||
skb_queue_tail(&pshm_drv->sk_qhead, skb);
|
||||
|
||||
/* Schedule Tx work queue. for deferred processing of skbs*/
|
||||
if (!work_pending(&pshm_drv->shm_tx_work))
|
||||
queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops netdev_ops = {
|
||||
.ndo_open = shm_netdev_open,
|
||||
.ndo_stop = shm_netdev_close,
|
||||
.ndo_start_xmit = shm_netdev_tx,
|
||||
};
|
||||
|
||||
static void shm_netdev_setup(struct net_device *pshm_netdev)
|
||||
{
|
||||
struct shmdrv_layer *pshm_drv;
|
||||
pshm_netdev->netdev_ops = &netdev_ops;
|
||||
|
||||
pshm_netdev->mtu = CAIF_MAX_MTU;
|
||||
pshm_netdev->type = ARPHRD_CAIF;
|
||||
pshm_netdev->hard_header_len = CAIF_NEEDED_HEADROOM;
|
||||
pshm_netdev->tx_queue_len = 0;
|
||||
pshm_netdev->destructor = free_netdev;
|
||||
|
||||
pshm_drv = netdev_priv(pshm_netdev);
|
||||
|
||||
/* Initialize structures in a clean state. */
|
||||
memset(pshm_drv, 0, sizeof(struct shmdrv_layer));
|
||||
|
||||
pshm_drv->cfdev.link_select = CAIF_LINK_LOW_LATENCY;
|
||||
}
|
||||
|
||||
int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
|
||||
{
|
||||
int result, j;
|
||||
struct shmdrv_layer *pshm_drv = NULL;
|
||||
|
||||
pshm_dev->pshm_netdev = alloc_netdev(sizeof(struct shmdrv_layer),
|
||||
"cfshm%d", shm_netdev_setup);
|
||||
if (!pshm_dev->pshm_netdev)
|
||||
return -ENOMEM;
|
||||
|
||||
pshm_drv = netdev_priv(pshm_dev->pshm_netdev);
|
||||
pshm_drv->pshm_dev = pshm_dev;
|
||||
|
||||
/*
|
||||
* Initialization starts with the verification of the
|
||||
* availability of MBX driver by calling its setup function.
|
||||
* MBX driver must be available by this time for proper
|
||||
* functioning of SHM driver.
|
||||
*/
|
||||
if ((pshm_dev->pshmdev_mbxsetup
|
||||
(caif_shmdrv_rx_cb, pshm_dev, pshm_drv)) != 0) {
|
||||
pr_warn("Could not config. SHM Mailbox,"
|
||||
" Bailing out.....\n");
|
||||
free_netdev(pshm_dev->pshm_netdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
skb_queue_head_init(&pshm_drv->sk_qhead);
|
||||
|
||||
pr_info("SHM DEVICE[%d] PROBED BY DRIVER, NEW SHM DRIVER"
|
||||
" INSTANCE AT pshm_drv =0x%p\n",
|
||||
pshm_drv->pshm_dev->shm_id, pshm_drv);
|
||||
|
||||
if (pshm_dev->shm_total_sz <
|
||||
(NR_TX_BUF * TX_BUF_SZ + NR_RX_BUF * RX_BUF_SZ)) {
|
||||
|
||||
pr_warn("ERROR, Amount of available"
|
||||
" Phys. SHM cannot accommodate current SHM "
|
||||
"driver configuration, Bailing out ...\n");
|
||||
free_netdev(pshm_dev->pshm_netdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pshm_drv->shm_base_addr = pshm_dev->shm_base_addr;
|
||||
pshm_drv->shm_tx_addr = pshm_drv->shm_base_addr;
|
||||
|
||||
if (pshm_dev->shm_loopback)
|
||||
pshm_drv->shm_rx_addr = pshm_drv->shm_tx_addr;
|
||||
else
|
||||
pshm_drv->shm_rx_addr = pshm_dev->shm_base_addr +
|
||||
(NR_TX_BUF * TX_BUF_SZ);
|
||||
|
||||
spin_lock_init(&pshm_drv->lock);
|
||||
INIT_LIST_HEAD(&pshm_drv->tx_empty_list);
|
||||
INIT_LIST_HEAD(&pshm_drv->tx_pend_list);
|
||||
INIT_LIST_HEAD(&pshm_drv->tx_full_list);
|
||||
|
||||
INIT_LIST_HEAD(&pshm_drv->rx_empty_list);
|
||||
INIT_LIST_HEAD(&pshm_drv->rx_pend_list);
|
||||
INIT_LIST_HEAD(&pshm_drv->rx_full_list);
|
||||
|
||||
INIT_WORK(&pshm_drv->shm_tx_work, shm_tx_work_func);
|
||||
INIT_WORK(&pshm_drv->shm_rx_work, shm_rx_work_func);
|
||||
|
||||
pshm_drv->pshm_tx_workqueue =
|
||||
create_singlethread_workqueue("shm_tx_work");
|
||||
pshm_drv->pshm_rx_workqueue =
|
||||
create_singlethread_workqueue("shm_rx_work");
|
||||
|
||||
for (j = 0; j < NR_TX_BUF; j++) {
|
||||
struct buf_list *tx_buf =
|
||||
kmalloc(sizeof(struct buf_list), GFP_KERNEL);
|
||||
|
||||
if (tx_buf == NULL) {
|
||||
free_netdev(pshm_dev->pshm_netdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
tx_buf->index = j;
|
||||
tx_buf->phy_addr = pshm_drv->shm_tx_addr + (TX_BUF_SZ * j);
|
||||
tx_buf->len = TX_BUF_SZ;
|
||||
tx_buf->frames = 0;
|
||||
tx_buf->frm_ofs = SHM_CAIF_FRM_OFS;
|
||||
|
||||
if (pshm_dev->shm_loopback)
|
||||
tx_buf->desc_vptr = (unsigned char *)tx_buf->phy_addr;
|
||||
else
|
||||
/*
|
||||
* FIXME: the result of ioremap is not a pointer - arnd
|
||||
*/
|
||||
tx_buf->desc_vptr =
|
||||
ioremap(tx_buf->phy_addr, TX_BUF_SZ);
|
||||
|
||||
list_add_tail(&tx_buf->list, &pshm_drv->tx_empty_list);
|
||||
}
|
||||
|
||||
for (j = 0; j < NR_RX_BUF; j++) {
|
||||
struct buf_list *rx_buf =
|
||||
kmalloc(sizeof(struct buf_list), GFP_KERNEL);
|
||||
|
||||
if (rx_buf == NULL) {
|
||||
free_netdev(pshm_dev->pshm_netdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
rx_buf->index = j;
|
||||
rx_buf->phy_addr = pshm_drv->shm_rx_addr + (RX_BUF_SZ * j);
|
||||
rx_buf->len = RX_BUF_SZ;
|
||||
|
||||
if (pshm_dev->shm_loopback)
|
||||
rx_buf->desc_vptr = (unsigned char *)rx_buf->phy_addr;
|
||||
else
|
||||
rx_buf->desc_vptr =
|
||||
ioremap(rx_buf->phy_addr, RX_BUF_SZ);
|
||||
list_add_tail(&rx_buf->list, &pshm_drv->rx_empty_list);
|
||||
}
|
||||
|
||||
pshm_drv->tx_empty_available = 1;
|
||||
result = register_netdev(pshm_dev->pshm_netdev);
|
||||
if (result)
|
||||
pr_warn("ERROR[%d], SHM could not, "
|
||||
"register with NW FRMWK Bailing out ...\n", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void caif_shmcore_remove(struct net_device *pshm_netdev)
|
||||
{
|
||||
struct buf_list *pbuf;
|
||||
struct shmdrv_layer *pshm_drv = NULL;
|
||||
|
||||
pshm_drv = netdev_priv(pshm_netdev);
|
||||
|
||||
while (!(list_empty(&pshm_drv->tx_pend_list))) {
|
||||
pbuf =
|
||||
list_entry(pshm_drv->tx_pend_list.next,
|
||||
struct buf_list, list);
|
||||
|
||||
list_del(&pbuf->list);
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
||||
while (!(list_empty(&pshm_drv->tx_full_list))) {
|
||||
pbuf =
|
||||
list_entry(pshm_drv->tx_full_list.next,
|
||||
struct buf_list, list);
|
||||
list_del(&pbuf->list);
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
||||
while (!(list_empty(&pshm_drv->tx_empty_list))) {
|
||||
pbuf =
|
||||
list_entry(pshm_drv->tx_empty_list.next,
|
||||
struct buf_list, list);
|
||||
list_del(&pbuf->list);
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
||||
while (!(list_empty(&pshm_drv->rx_full_list))) {
|
||||
pbuf =
|
||||
list_entry(pshm_drv->tx_full_list.next,
|
||||
struct buf_list, list);
|
||||
list_del(&pbuf->list);
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
||||
while (!(list_empty(&pshm_drv->rx_pend_list))) {
|
||||
pbuf =
|
||||
list_entry(pshm_drv->tx_pend_list.next,
|
||||
struct buf_list, list);
|
||||
list_del(&pbuf->list);
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
||||
while (!(list_empty(&pshm_drv->rx_empty_list))) {
|
||||
pbuf =
|
||||
list_entry(pshm_drv->rx_empty_list.next,
|
||||
struct buf_list, list);
|
||||
list_del(&pbuf->list);
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
||||
/* Destroy work queues. */
|
||||
destroy_workqueue(pshm_drv->pshm_tx_workqueue);
|
||||
destroy_workqueue(pshm_drv->pshm_rx_workqueue);
|
||||
|
||||
unregister_netdev(pshm_netdev);
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson AB 2010
|
||||
* Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
|
||||
* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
|
||||
* Author: Daniel Martensson
|
||||
* License terms: GNU General Public License (GPL) version 2.
|
||||
*/
|
||||
|
||||
@ -29,7 +28,7 @@
|
||||
#endif /* CONFIG_CAIF_SPI_SYNC */
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Daniel Martensson<daniel.martensson@stericsson.com>");
|
||||
MODULE_AUTHOR("Daniel Martensson");
|
||||
MODULE_DESCRIPTION("CAIF SPI driver");
|
||||
|
||||
/* Returns the number of padding bytes for alignment. */
|
||||
@ -864,6 +863,7 @@ static int __init cfspi_init_module(void)
|
||||
driver_remove_file(&cfspi_spi_driver.driver,
|
||||
&driver_attr_up_head_align);
|
||||
err_create_up_head_align:
|
||||
platform_driver_unregister(&cfspi_spi_driver);
|
||||
err_dev_register:
|
||||
return result;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson AB 2010
|
||||
* Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
|
||||
* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
|
||||
* Author: Daniel Martensson
|
||||
* License terms: GNU General Public License (GPL) version 2.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
|
@ -65,7 +65,7 @@ config CAN_LEDS
|
||||
|
||||
config CAN_AT91
|
||||
tristate "Atmel AT91 onchip CAN controller"
|
||||
depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9X5
|
||||
depends on ARM
|
||||
---help---
|
||||
This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
|
||||
and AT91SAM9X5 processors.
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
@ -155,19 +156,20 @@ struct at91_priv {
|
||||
canid_t mb0_id;
|
||||
};
|
||||
|
||||
static const struct at91_devtype_data at91_devtype_data[] = {
|
||||
[AT91_DEVTYPE_SAM9263] = {
|
||||
.rx_first = 1,
|
||||
.rx_split = 8,
|
||||
.rx_last = 11,
|
||||
.tx_shift = 2,
|
||||
},
|
||||
[AT91_DEVTYPE_SAM9X5] = {
|
||||
.rx_first = 0,
|
||||
.rx_split = 4,
|
||||
.rx_last = 5,
|
||||
.tx_shift = 1,
|
||||
},
|
||||
static const struct at91_devtype_data at91_at91sam9263_data = {
|
||||
.rx_first = 1,
|
||||
.rx_split = 8,
|
||||
.rx_last = 11,
|
||||
.tx_shift = 2,
|
||||
.type = AT91_DEVTYPE_SAM9263,
|
||||
};
|
||||
|
||||
static const struct at91_devtype_data at91_at91sam9x5_data = {
|
||||
.rx_first = 0,
|
||||
.rx_split = 4,
|
||||
.rx_last = 5,
|
||||
.tx_shift = 1,
|
||||
.type = AT91_DEVTYPE_SAM9X5,
|
||||
};
|
||||
|
||||
static const struct can_bittiming_const at91_bittiming_const = {
|
||||
@ -1249,10 +1251,42 @@ static struct attribute_group at91_sysfs_attr_group = {
|
||||
.attrs = at91_sysfs_attrs,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id at91_can_dt_ids[] = {
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-can",
|
||||
.data = &at91_at91sam9x5_data,
|
||||
}, {
|
||||
.compatible = "atmel,at91sam9263-can",
|
||||
.data = &at91_at91sam9263_data,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, at91_can_dt_ids);
|
||||
#else
|
||||
#define at91_can_dt_ids NULL
|
||||
#endif
|
||||
|
||||
static const struct at91_devtype_data *at91_can_get_driver_data(struct platform_device *pdev)
|
||||
{
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(at91_can_dt_ids, pdev->dev.of_node);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "no matching node found in dtb\n");
|
||||
return NULL;
|
||||
}
|
||||
return (const struct at91_devtype_data *)match->data;
|
||||
}
|
||||
return (const struct at91_devtype_data *)
|
||||
platform_get_device_id(pdev)->driver_data;
|
||||
}
|
||||
|
||||
static int at91_can_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct at91_devtype_data *devtype_data;
|
||||
enum at91_devtype devtype;
|
||||
struct net_device *dev;
|
||||
struct at91_priv *priv;
|
||||
struct resource *res;
|
||||
@ -1260,8 +1294,12 @@ static int at91_can_probe(struct platform_device *pdev)
|
||||
void __iomem *addr;
|
||||
int err, irq;
|
||||
|
||||
devtype = pdev->id_entry->driver_data;
|
||||
devtype_data = &at91_devtype_data[devtype];
|
||||
devtype_data = at91_can_get_driver_data(pdev);
|
||||
if (!devtype_data) {
|
||||
dev_err(&pdev->dev, "no driver data\n");
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "can_clk");
|
||||
if (IS_ERR(clk)) {
|
||||
@ -1310,7 +1348,6 @@ static int at91_can_probe(struct platform_device *pdev)
|
||||
priv->dev = dev;
|
||||
priv->reg_base = addr;
|
||||
priv->devtype_data = *devtype_data;
|
||||
priv->devtype_data.type = devtype;
|
||||
priv->clk = clk;
|
||||
priv->pdata = pdev->dev.platform_data;
|
||||
priv->mb0_id = 0x7ff;
|
||||
@ -1373,10 +1410,10 @@ static int at91_can_remove(struct platform_device *pdev)
|
||||
static const struct platform_device_id at91_can_id_table[] = {
|
||||
{
|
||||
.name = "at91_can",
|
||||
.driver_data = AT91_DEVTYPE_SAM9263,
|
||||
.driver_data = (kernel_ulong_t)&at91_at91sam9x5_data,
|
||||
}, {
|
||||
.name = "at91sam9x5_can",
|
||||
.driver_data = AT91_DEVTYPE_SAM9X5,
|
||||
.driver_data = (kernel_ulong_t)&at91_at91sam9263_data,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
@ -1389,6 +1426,7 @@ static struct platform_driver at91_can_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = at91_can_dt_ids,
|
||||
},
|
||||
.id_table = at91_can_id_table,
|
||||
};
|
||||
|
@ -412,7 +412,7 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
irqreturn_t bfin_can_interrupt(int irq, void *dev_id)
|
||||
static irqreturn_t bfin_can_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct bfin_can_priv *priv = netdev_priv(dev);
|
||||
@ -504,7 +504,7 @@ static int bfin_can_close(struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct net_device *alloc_bfin_candev(void)
|
||||
static struct net_device *alloc_bfin_candev(void)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct bfin_can_priv *priv;
|
||||
|
@ -269,7 +269,7 @@ struct mcp251x_priv {
|
||||
#define MCP251X_IS(_model) \
|
||||
static inline int mcp251x_is_##_model(struct spi_device *spi) \
|
||||
{ \
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); \
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi); \
|
||||
return priv->model == CAN_MCP251X_MCP##_model; \
|
||||
}
|
||||
|
||||
@ -305,7 +305,7 @@ static void mcp251x_clean(struct net_device *net)
|
||||
*/
|
||||
static int mcp251x_spi_trans(struct spi_device *spi, int len)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = priv->spi_tx_buf,
|
||||
.rx_buf = priv->spi_rx_buf,
|
||||
@ -333,7 +333,7 @@ static int mcp251x_spi_trans(struct spi_device *spi, int len)
|
||||
|
||||
static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
u8 val = 0;
|
||||
|
||||
priv->spi_tx_buf[0] = INSTRUCTION_READ;
|
||||
@ -348,7 +348,7 @@ static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
|
||||
static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg,
|
||||
uint8_t *v1, uint8_t *v2)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
priv->spi_tx_buf[0] = INSTRUCTION_READ;
|
||||
priv->spi_tx_buf[1] = reg;
|
||||
@ -361,7 +361,7 @@ static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg,
|
||||
|
||||
static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
priv->spi_tx_buf[0] = INSTRUCTION_WRITE;
|
||||
priv->spi_tx_buf[1] = reg;
|
||||
@ -373,7 +373,7 @@ static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
|
||||
static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
|
||||
u8 mask, uint8_t val)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;
|
||||
priv->spi_tx_buf[1] = reg;
|
||||
@ -386,7 +386,7 @@ static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
|
||||
static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
|
||||
int len, int tx_buf_idx)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
if (mcp251x_is_2510(spi)) {
|
||||
int i;
|
||||
@ -403,7 +403,7 @@ static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
|
||||
static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
|
||||
int tx_buf_idx)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
u32 sid, eid, exide, rtr;
|
||||
u8 buf[SPI_TRANSFER_BUF_LEN];
|
||||
|
||||
@ -434,7 +434,7 @@ static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
|
||||
static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
|
||||
int buf_idx)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
if (mcp251x_is_2510(spi)) {
|
||||
int i, len;
|
||||
@ -454,7 +454,7 @@ static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
|
||||
|
||||
static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
struct sk_buff *skb;
|
||||
struct can_frame *frame;
|
||||
u8 buf[SPI_TRANSFER_BUF_LEN];
|
||||
@ -550,7 +550,7 @@ static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
|
||||
|
||||
static int mcp251x_set_normal_mode(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
unsigned long timeout;
|
||||
|
||||
/* Enable interrupts */
|
||||
@ -620,7 +620,7 @@ static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
|
||||
|
||||
static int mcp251x_hw_reset(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
int ret;
|
||||
unsigned long timeout;
|
||||
|
||||
@ -1026,7 +1026,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
||||
CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY;
|
||||
priv->model = spi_get_device_id(spi)->driver_data;
|
||||
priv->net = net;
|
||||
dev_set_drvdata(&spi->dev, priv);
|
||||
spi_set_drvdata(spi, priv);
|
||||
|
||||
priv->spi = spi;
|
||||
mutex_init(&priv->mcp_lock);
|
||||
@ -1124,7 +1124,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
||||
static int mcp251x_can_remove(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_platform_data *pdata = spi->dev.platform_data;
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
struct net_device *net = priv->net;
|
||||
|
||||
unregister_candev(net);
|
||||
@ -1144,11 +1144,13 @@ static int mcp251x_can_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int mcp251x_can_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct mcp251x_platform_data *pdata = spi->dev.platform_data;
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
struct net_device *net = priv->net;
|
||||
|
||||
priv->force_quit = 1;
|
||||
@ -1176,10 +1178,11 @@ static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp251x_can_resume(struct spi_device *spi)
|
||||
static int mcp251x_can_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct mcp251x_platform_data *pdata = spi->dev.platform_data;
|
||||
struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
if (priv->after_suspend & AFTER_SUSPEND_POWER) {
|
||||
pdata->power_enable(1);
|
||||
@ -1197,11 +1200,11 @@ static int mcp251x_can_resume(struct spi_device *spi)
|
||||
enable_irq(spi->irq);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define mcp251x_can_suspend NULL
|
||||
#define mcp251x_can_resume NULL
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mcp251x_can_pm_ops, mcp251x_can_suspend,
|
||||
mcp251x_can_resume);
|
||||
|
||||
static const struct spi_device_id mcp251x_id_table[] = {
|
||||
{ "mcp2510", CAN_MCP251X_MCP2510 },
|
||||
{ "mcp2515", CAN_MCP251X_MCP2515 },
|
||||
@ -1213,29 +1216,15 @@ MODULE_DEVICE_TABLE(spi, mcp251x_id_table);
|
||||
static struct spi_driver mcp251x_can_driver = {
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mcp251x_can_pm_ops,
|
||||
},
|
||||
|
||||
.id_table = mcp251x_id_table,
|
||||
.probe = mcp251x_can_probe,
|
||||
.remove = mcp251x_can_remove,
|
||||
.suspend = mcp251x_can_suspend,
|
||||
.resume = mcp251x_can_resume,
|
||||
};
|
||||
|
||||
static int __init mcp251x_can_init(void)
|
||||
{
|
||||
return spi_register_driver(&mcp251x_can_driver);
|
||||
}
|
||||
|
||||
static void __exit mcp251x_can_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&mcp251x_can_driver);
|
||||
}
|
||||
|
||||
module_init(mcp251x_can_init);
|
||||
module_exit(mcp251x_can_exit);
|
||||
module_spi_driver(mcp251x_can_driver);
|
||||
|
||||
MODULE_AUTHOR("Chris Elston <celston@katalix.com>, "
|
||||
"Christian Pellegrin <chripell@evolware.org>");
|
||||
|
@ -168,12 +168,12 @@ static inline int ems_pci_check_chan(const struct sja1000_priv *priv)
|
||||
unsigned char res;
|
||||
|
||||
/* Make sure SJA1000 is in reset mode */
|
||||
priv->write_reg(priv, REG_MOD, 1);
|
||||
priv->write_reg(priv, SJA1000_MOD, 1);
|
||||
|
||||
priv->write_reg(priv, REG_CDR, CDR_PELICAN);
|
||||
priv->write_reg(priv, SJA1000_CDR, CDR_PELICAN);
|
||||
|
||||
/* read reset-values */
|
||||
res = priv->read_reg(priv, REG_CDR);
|
||||
res = priv->read_reg(priv, SJA1000_CDR);
|
||||
|
||||
if (res == CDR_PELICAN)
|
||||
return 1;
|
||||
|
@ -126,11 +126,11 @@ static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id)
|
||||
static inline int ems_pcmcia_check_chan(struct sja1000_priv *priv)
|
||||
{
|
||||
/* Make sure SJA1000 is in reset mode */
|
||||
ems_pcmcia_write_reg(priv, REG_MOD, 1);
|
||||
ems_pcmcia_write_reg(priv, REG_CDR, CDR_PELICAN);
|
||||
ems_pcmcia_write_reg(priv, SJA1000_MOD, 1);
|
||||
ems_pcmcia_write_reg(priv, SJA1000_CDR, CDR_PELICAN);
|
||||
|
||||
/* read reset-values */
|
||||
if (ems_pcmcia_read_reg(priv, REG_CDR) == CDR_PELICAN)
|
||||
if (ems_pcmcia_read_reg(priv, SJA1000_CDR) == CDR_PELICAN)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -159,9 +159,9 @@ static int number_of_sja1000_chip(void __iomem *base_addr)
|
||||
for (i = 0; i < MAX_NO_OF_CHANNELS; i++) {
|
||||
/* reset chip */
|
||||
iowrite8(MOD_RM, base_addr +
|
||||
(i * KVASER_PCI_PORT_BYTES) + REG_MOD);
|
||||
(i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD);
|
||||
status = ioread8(base_addr +
|
||||
(i * KVASER_PCI_PORT_BYTES) + REG_MOD);
|
||||
(i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD);
|
||||
/* check reset bit */
|
||||
if (!(status & MOD_RM))
|
||||
break;
|
||||
|
@ -402,7 +402,7 @@ static void peak_pciec_write_reg(const struct sja1000_priv *priv,
|
||||
int c = (priv->reg_base - card->reg_base) / PEAK_PCI_CHAN_SIZE;
|
||||
|
||||
/* sja1000 register changes control the leds state */
|
||||
if (port == REG_MOD)
|
||||
if (port == SJA1000_MOD)
|
||||
switch (val) {
|
||||
case MOD_RM:
|
||||
/* Reset Mode: set led on */
|
||||
|
@ -196,7 +196,7 @@ static void pcan_write_canreg(const struct sja1000_priv *priv, int port, u8 v)
|
||||
int c = (priv->reg_base - card->ioport_addr) / PCC_CHAN_SIZE;
|
||||
|
||||
/* sja1000 register changes control the leds state */
|
||||
if (port == REG_MOD)
|
||||
if (port == SJA1000_MOD)
|
||||
switch (v) {
|
||||
case MOD_RM:
|
||||
/* Reset Mode: set led on */
|
||||
@ -509,11 +509,11 @@ static void pcan_free_channels(struct pcan_pccard *card)
|
||||
static inline int pcan_channel_present(struct sja1000_priv *priv)
|
||||
{
|
||||
/* make sure SJA1000 is in reset mode */
|
||||
pcan_write_canreg(priv, REG_MOD, 1);
|
||||
pcan_write_canreg(priv, REG_CDR, CDR_PELICAN);
|
||||
pcan_write_canreg(priv, SJA1000_MOD, 1);
|
||||
pcan_write_canreg(priv, SJA1000_CDR, CDR_PELICAN);
|
||||
|
||||
/* read reset-values */
|
||||
if (pcan_read_canreg(priv, REG_CDR) == CDR_PELICAN)
|
||||
if (pcan_read_canreg(priv, SJA1000_CDR) == CDR_PELICAN)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -348,20 +348,20 @@ static inline int plx_pci_check_sja1000(const struct sja1000_priv *priv)
|
||||
*/
|
||||
if ((priv->read_reg(priv, REG_CR) & REG_CR_BASICCAN_INITIAL_MASK) ==
|
||||
REG_CR_BASICCAN_INITIAL &&
|
||||
(priv->read_reg(priv, SJA1000_REG_SR) == REG_SR_BASICCAN_INITIAL) &&
|
||||
(priv->read_reg(priv, REG_IR) == REG_IR_BASICCAN_INITIAL))
|
||||
(priv->read_reg(priv, SJA1000_SR) == REG_SR_BASICCAN_INITIAL) &&
|
||||
(priv->read_reg(priv, SJA1000_IR) == REG_IR_BASICCAN_INITIAL))
|
||||
flag = 1;
|
||||
|
||||
/* Bring the SJA1000 into the PeliCAN mode*/
|
||||
priv->write_reg(priv, REG_CDR, CDR_PELICAN);
|
||||
priv->write_reg(priv, SJA1000_CDR, CDR_PELICAN);
|
||||
|
||||
/*
|
||||
* Check registers after reset in the PeliCAN mode.
|
||||
* See states on p. 23 of the Datasheet.
|
||||
*/
|
||||
if (priv->read_reg(priv, REG_MOD) == REG_MOD_PELICAN_INITIAL &&
|
||||
priv->read_reg(priv, SJA1000_REG_SR) == REG_SR_PELICAN_INITIAL &&
|
||||
priv->read_reg(priv, REG_IR) == REG_IR_PELICAN_INITIAL)
|
||||
if (priv->read_reg(priv, SJA1000_MOD) == REG_MOD_PELICAN_INITIAL &&
|
||||
priv->read_reg(priv, SJA1000_SR) == REG_SR_PELICAN_INITIAL &&
|
||||
priv->read_reg(priv, SJA1000_IR) == REG_IR_PELICAN_INITIAL)
|
||||
return flag;
|
||||
|
||||
return 0;
|
||||
|
@ -91,14 +91,14 @@ static void sja1000_write_cmdreg(struct sja1000_priv *priv, u8 val)
|
||||
* the write_reg() operation - especially on SMP systems.
|
||||
*/
|
||||
spin_lock_irqsave(&priv->cmdreg_lock, flags);
|
||||
priv->write_reg(priv, REG_CMR, val);
|
||||
priv->read_reg(priv, SJA1000_REG_SR);
|
||||
priv->write_reg(priv, SJA1000_CMR, val);
|
||||
priv->read_reg(priv, SJA1000_SR);
|
||||
spin_unlock_irqrestore(&priv->cmdreg_lock, flags);
|
||||
}
|
||||
|
||||
static int sja1000_is_absent(struct sja1000_priv *priv)
|
||||
{
|
||||
return (priv->read_reg(priv, REG_MOD) == 0xFF);
|
||||
return (priv->read_reg(priv, SJA1000_MOD) == 0xFF);
|
||||
}
|
||||
|
||||
static int sja1000_probe_chip(struct net_device *dev)
|
||||
@ -116,11 +116,11 @@ static int sja1000_probe_chip(struct net_device *dev)
|
||||
static void set_reset_mode(struct net_device *dev)
|
||||
{
|
||||
struct sja1000_priv *priv = netdev_priv(dev);
|
||||
unsigned char status = priv->read_reg(priv, REG_MOD);
|
||||
unsigned char status = priv->read_reg(priv, SJA1000_MOD);
|
||||
int i;
|
||||
|
||||
/* disable interrupts */
|
||||
priv->write_reg(priv, REG_IER, IRQ_OFF);
|
||||
priv->write_reg(priv, SJA1000_IER, IRQ_OFF);
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
/* check reset bit */
|
||||
@ -129,9 +129,10 @@ static void set_reset_mode(struct net_device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
priv->write_reg(priv, REG_MOD, MOD_RM); /* reset chip */
|
||||
/* reset chip */
|
||||
priv->write_reg(priv, SJA1000_MOD, MOD_RM);
|
||||
udelay(10);
|
||||
status = priv->read_reg(priv, REG_MOD);
|
||||
status = priv->read_reg(priv, SJA1000_MOD);
|
||||
}
|
||||
|
||||
netdev_err(dev, "setting SJA1000 into reset mode failed!\n");
|
||||
@ -140,7 +141,7 @@ static void set_reset_mode(struct net_device *dev)
|
||||
static void set_normal_mode(struct net_device *dev)
|
||||
{
|
||||
struct sja1000_priv *priv = netdev_priv(dev);
|
||||
unsigned char status = priv->read_reg(priv, REG_MOD);
|
||||
unsigned char status = priv->read_reg(priv, SJA1000_MOD);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
@ -149,22 +150,22 @@ static void set_normal_mode(struct net_device *dev)
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
/* enable interrupts */
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
|
||||
priv->write_reg(priv, REG_IER, IRQ_ALL);
|
||||
priv->write_reg(priv, SJA1000_IER, IRQ_ALL);
|
||||
else
|
||||
priv->write_reg(priv, REG_IER,
|
||||
priv->write_reg(priv, SJA1000_IER,
|
||||
IRQ_ALL & ~IRQ_BEI);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set chip to normal mode */
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||
priv->write_reg(priv, REG_MOD, MOD_LOM);
|
||||
priv->write_reg(priv, SJA1000_MOD, MOD_LOM);
|
||||
else
|
||||
priv->write_reg(priv, REG_MOD, 0x00);
|
||||
priv->write_reg(priv, SJA1000_MOD, 0x00);
|
||||
|
||||
udelay(10);
|
||||
|
||||
status = priv->read_reg(priv, REG_MOD);
|
||||
status = priv->read_reg(priv, SJA1000_MOD);
|
||||
}
|
||||
|
||||
netdev_err(dev, "setting SJA1000 into normal mode failed!\n");
|
||||
@ -179,9 +180,9 @@ static void sja1000_start(struct net_device *dev)
|
||||
set_reset_mode(dev);
|
||||
|
||||
/* Clear error counters and error code capture */
|
||||
priv->write_reg(priv, REG_TXERR, 0x0);
|
||||
priv->write_reg(priv, REG_RXERR, 0x0);
|
||||
priv->read_reg(priv, REG_ECC);
|
||||
priv->write_reg(priv, SJA1000_TXERR, 0x0);
|
||||
priv->write_reg(priv, SJA1000_RXERR, 0x0);
|
||||
priv->read_reg(priv, SJA1000_ECC);
|
||||
|
||||
/* leave reset mode */
|
||||
set_normal_mode(dev);
|
||||
@ -217,8 +218,8 @@ static int sja1000_set_bittiming(struct net_device *dev)
|
||||
|
||||
netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
|
||||
|
||||
priv->write_reg(priv, REG_BTR0, btr0);
|
||||
priv->write_reg(priv, REG_BTR1, btr1);
|
||||
priv->write_reg(priv, SJA1000_BTR0, btr0);
|
||||
priv->write_reg(priv, SJA1000_BTR1, btr1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -228,8 +229,8 @@ static int sja1000_get_berr_counter(const struct net_device *dev,
|
||||
{
|
||||
struct sja1000_priv *priv = netdev_priv(dev);
|
||||
|
||||
bec->txerr = priv->read_reg(priv, REG_TXERR);
|
||||
bec->rxerr = priv->read_reg(priv, REG_RXERR);
|
||||
bec->txerr = priv->read_reg(priv, SJA1000_TXERR);
|
||||
bec->rxerr = priv->read_reg(priv, SJA1000_RXERR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -247,20 +248,20 @@ static void chipset_init(struct net_device *dev)
|
||||
struct sja1000_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* set clock divider and output control register */
|
||||
priv->write_reg(priv, REG_CDR, priv->cdr | CDR_PELICAN);
|
||||
priv->write_reg(priv, SJA1000_CDR, priv->cdr | CDR_PELICAN);
|
||||
|
||||
/* set acceptance filter (accept all) */
|
||||
priv->write_reg(priv, REG_ACCC0, 0x00);
|
||||
priv->write_reg(priv, REG_ACCC1, 0x00);
|
||||
priv->write_reg(priv, REG_ACCC2, 0x00);
|
||||
priv->write_reg(priv, REG_ACCC3, 0x00);
|
||||
priv->write_reg(priv, SJA1000_ACCC0, 0x00);
|
||||
priv->write_reg(priv, SJA1000_ACCC1, 0x00);
|
||||
priv->write_reg(priv, SJA1000_ACCC2, 0x00);
|
||||
priv->write_reg(priv, SJA1000_ACCC3, 0x00);
|
||||
|
||||
priv->write_reg(priv, REG_ACCM0, 0xFF);
|
||||
priv->write_reg(priv, REG_ACCM1, 0xFF);
|
||||
priv->write_reg(priv, REG_ACCM2, 0xFF);
|
||||
priv->write_reg(priv, REG_ACCM3, 0xFF);
|
||||
priv->write_reg(priv, SJA1000_ACCM0, 0xFF);
|
||||
priv->write_reg(priv, SJA1000_ACCM1, 0xFF);
|
||||
priv->write_reg(priv, SJA1000_ACCM2, 0xFF);
|
||||
priv->write_reg(priv, SJA1000_ACCM3, 0xFF);
|
||||
|
||||
priv->write_reg(priv, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
|
||||
priv->write_reg(priv, SJA1000_OCR, priv->ocr | OCR_MODE_NORMAL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -289,21 +290,21 @@ static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb,
|
||||
id = cf->can_id;
|
||||
|
||||
if (id & CAN_RTR_FLAG)
|
||||
fi |= FI_RTR;
|
||||
fi |= SJA1000_FI_RTR;
|
||||
|
||||
if (id & CAN_EFF_FLAG) {
|
||||
fi |= FI_FF;
|
||||
dreg = EFF_BUF;
|
||||
priv->write_reg(priv, REG_FI, fi);
|
||||
priv->write_reg(priv, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
|
||||
priv->write_reg(priv, REG_ID2, (id & 0x001fe000) >> (5 + 8));
|
||||
priv->write_reg(priv, REG_ID3, (id & 0x00001fe0) >> 5);
|
||||
priv->write_reg(priv, REG_ID4, (id & 0x0000001f) << 3);
|
||||
fi |= SJA1000_FI_FF;
|
||||
dreg = SJA1000_EFF_BUF;
|
||||
priv->write_reg(priv, SJA1000_FI, fi);
|
||||
priv->write_reg(priv, SJA1000_ID1, (id & 0x1fe00000) >> 21);
|
||||
priv->write_reg(priv, SJA1000_ID2, (id & 0x001fe000) >> 13);
|
||||
priv->write_reg(priv, SJA1000_ID3, (id & 0x00001fe0) >> 5);
|
||||
priv->write_reg(priv, SJA1000_ID4, (id & 0x0000001f) << 3);
|
||||
} else {
|
||||
dreg = SFF_BUF;
|
||||
priv->write_reg(priv, REG_FI, fi);
|
||||
priv->write_reg(priv, REG_ID1, (id & 0x000007f8) >> 3);
|
||||
priv->write_reg(priv, REG_ID2, (id & 0x00000007) << 5);
|
||||
dreg = SJA1000_SFF_BUF;
|
||||
priv->write_reg(priv, SJA1000_FI, fi);
|
||||
priv->write_reg(priv, SJA1000_ID1, (id & 0x000007f8) >> 3);
|
||||
priv->write_reg(priv, SJA1000_ID2, (id & 0x00000007) << 5);
|
||||
}
|
||||
|
||||
for (i = 0; i < dlc; i++)
|
||||
@ -335,25 +336,25 @@ static void sja1000_rx(struct net_device *dev)
|
||||
if (skb == NULL)
|
||||
return;
|
||||
|
||||
fi = priv->read_reg(priv, REG_FI);
|
||||
fi = priv->read_reg(priv, SJA1000_FI);
|
||||
|
||||
if (fi & FI_FF) {
|
||||
if (fi & SJA1000_FI_FF) {
|
||||
/* extended frame format (EFF) */
|
||||
dreg = EFF_BUF;
|
||||
id = (priv->read_reg(priv, REG_ID1) << (5 + 16))
|
||||
| (priv->read_reg(priv, REG_ID2) << (5 + 8))
|
||||
| (priv->read_reg(priv, REG_ID3) << 5)
|
||||
| (priv->read_reg(priv, REG_ID4) >> 3);
|
||||
dreg = SJA1000_EFF_BUF;
|
||||
id = (priv->read_reg(priv, SJA1000_ID1) << 21)
|
||||
| (priv->read_reg(priv, SJA1000_ID2) << 13)
|
||||
| (priv->read_reg(priv, SJA1000_ID3) << 5)
|
||||
| (priv->read_reg(priv, SJA1000_ID4) >> 3);
|
||||
id |= CAN_EFF_FLAG;
|
||||
} else {
|
||||
/* standard frame format (SFF) */
|
||||
dreg = SFF_BUF;
|
||||
id = (priv->read_reg(priv, REG_ID1) << 3)
|
||||
| (priv->read_reg(priv, REG_ID2) >> 5);
|
||||
dreg = SJA1000_SFF_BUF;
|
||||
id = (priv->read_reg(priv, SJA1000_ID1) << 3)
|
||||
| (priv->read_reg(priv, SJA1000_ID2) >> 5);
|
||||
}
|
||||
|
||||
cf->can_dlc = get_can_dlc(fi & 0x0F);
|
||||
if (fi & FI_RTR) {
|
||||
if (fi & SJA1000_FI_RTR) {
|
||||
id |= CAN_RTR_FLAG;
|
||||
} else {
|
||||
for (i = 0; i < cf->can_dlc; i++)
|
||||
@ -414,7 +415,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
||||
priv->can.can_stats.bus_error++;
|
||||
stats->rx_errors++;
|
||||
|
||||
ecc = priv->read_reg(priv, REG_ECC);
|
||||
ecc = priv->read_reg(priv, SJA1000_ECC);
|
||||
|
||||
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
|
||||
|
||||
@ -448,7 +449,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
||||
if (isrc & IRQ_ALI) {
|
||||
/* arbitration lost interrupt */
|
||||
netdev_dbg(dev, "arbitration lost interrupt\n");
|
||||
alc = priv->read_reg(priv, REG_ALC);
|
||||
alc = priv->read_reg(priv, SJA1000_ALC);
|
||||
priv->can.can_stats.arbitration_lost++;
|
||||
stats->tx_errors++;
|
||||
cf->can_id |= CAN_ERR_LOSTARB;
|
||||
@ -457,8 +458,8 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
||||
|
||||
if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
|
||||
state == CAN_STATE_ERROR_PASSIVE)) {
|
||||
uint8_t rxerr = priv->read_reg(priv, REG_RXERR);
|
||||
uint8_t txerr = priv->read_reg(priv, REG_TXERR);
|
||||
uint8_t rxerr = priv->read_reg(priv, SJA1000_RXERR);
|
||||
uint8_t txerr = priv->read_reg(priv, SJA1000_TXERR);
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
if (state == CAN_STATE_ERROR_WARNING) {
|
||||
priv->can.can_stats.error_warning++;
|
||||
@ -494,15 +495,16 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
|
||||
int n = 0;
|
||||
|
||||
/* Shared interrupts and IRQ off? */
|
||||
if (priv->read_reg(priv, REG_IER) == IRQ_OFF)
|
||||
if (priv->read_reg(priv, SJA1000_IER) == IRQ_OFF)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (priv->pre_irq)
|
||||
priv->pre_irq(priv);
|
||||
|
||||
while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) {
|
||||
while ((isrc = priv->read_reg(priv, SJA1000_IR)) &&
|
||||
(n < SJA1000_MAX_IRQ)) {
|
||||
n++;
|
||||
status = priv->read_reg(priv, SJA1000_REG_SR);
|
||||
status = priv->read_reg(priv, SJA1000_SR);
|
||||
/* check for absent controller due to hw unplug */
|
||||
if (status == 0xFF && sja1000_is_absent(priv))
|
||||
return IRQ_NONE;
|
||||
@ -519,7 +521,7 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
|
||||
} else {
|
||||
/* transmission complete */
|
||||
stats->tx_bytes +=
|
||||
priv->read_reg(priv, REG_FI) & 0xf;
|
||||
priv->read_reg(priv, SJA1000_FI) & 0xf;
|
||||
stats->tx_packets++;
|
||||
can_get_echo_skb(dev, 0);
|
||||
}
|
||||
@ -530,7 +532,7 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
|
||||
/* receive interrupt */
|
||||
while (status & SR_RBS) {
|
||||
sja1000_rx(dev);
|
||||
status = priv->read_reg(priv, SJA1000_REG_SR);
|
||||
status = priv->read_reg(priv, SJA1000_SR);
|
||||
/* check for absent controller */
|
||||
if (status == 0xFF && sja1000_is_absent(priv))
|
||||
return IRQ_NONE;
|
||||
|
@ -54,46 +54,46 @@
|
||||
#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
|
||||
|
||||
/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
|
||||
#define REG_MOD 0x00
|
||||
#define REG_CMR 0x01
|
||||
#define SJA1000_REG_SR 0x02
|
||||
#define REG_IR 0x03
|
||||
#define REG_IER 0x04
|
||||
#define REG_ALC 0x0B
|
||||
#define REG_ECC 0x0C
|
||||
#define REG_EWL 0x0D
|
||||
#define REG_RXERR 0x0E
|
||||
#define REG_TXERR 0x0F
|
||||
#define REG_ACCC0 0x10
|
||||
#define REG_ACCC1 0x11
|
||||
#define REG_ACCC2 0x12
|
||||
#define REG_ACCC3 0x13
|
||||
#define REG_ACCM0 0x14
|
||||
#define REG_ACCM1 0x15
|
||||
#define REG_ACCM2 0x16
|
||||
#define REG_ACCM3 0x17
|
||||
#define REG_RMC 0x1D
|
||||
#define REG_RBSA 0x1E
|
||||
#define SJA1000_MOD 0x00
|
||||
#define SJA1000_CMR 0x01
|
||||
#define SJA1000_SR 0x02
|
||||
#define SJA1000_IR 0x03
|
||||
#define SJA1000_IER 0x04
|
||||
#define SJA1000_ALC 0x0B
|
||||
#define SJA1000_ECC 0x0C
|
||||
#define SJA1000_EWL 0x0D
|
||||
#define SJA1000_RXERR 0x0E
|
||||
#define SJA1000_TXERR 0x0F
|
||||
#define SJA1000_ACCC0 0x10
|
||||
#define SJA1000_ACCC1 0x11
|
||||
#define SJA1000_ACCC2 0x12
|
||||
#define SJA1000_ACCC3 0x13
|
||||
#define SJA1000_ACCM0 0x14
|
||||
#define SJA1000_ACCM1 0x15
|
||||
#define SJA1000_ACCM2 0x16
|
||||
#define SJA1000_ACCM3 0x17
|
||||
#define SJA1000_RMC 0x1D
|
||||
#define SJA1000_RBSA 0x1E
|
||||
|
||||
/* Common registers - manual section 6.5 */
|
||||
#define REG_BTR0 0x06
|
||||
#define REG_BTR1 0x07
|
||||
#define REG_OCR 0x08
|
||||
#define REG_CDR 0x1F
|
||||
#define SJA1000_BTR0 0x06
|
||||
#define SJA1000_BTR1 0x07
|
||||
#define SJA1000_OCR 0x08
|
||||
#define SJA1000_CDR 0x1F
|
||||
|
||||
#define REG_FI 0x10
|
||||
#define SFF_BUF 0x13
|
||||
#define EFF_BUF 0x15
|
||||
#define SJA1000_FI 0x10
|
||||
#define SJA1000_SFF_BUF 0x13
|
||||
#define SJA1000_EFF_BUF 0x15
|
||||
|
||||
#define FI_FF 0x80
|
||||
#define FI_RTR 0x40
|
||||
#define SJA1000_FI_FF 0x80
|
||||
#define SJA1000_FI_RTR 0x40
|
||||
|
||||
#define REG_ID1 0x11
|
||||
#define REG_ID2 0x12
|
||||
#define REG_ID3 0x13
|
||||
#define REG_ID4 0x14
|
||||
#define SJA1000_ID1 0x11
|
||||
#define SJA1000_ID2 0x12
|
||||
#define SJA1000_ID3 0x13
|
||||
#define SJA1000_ID4 0x14
|
||||
|
||||
#define CAN_RAM 0x20
|
||||
#define SJA1000_CAN_RAM 0x20
|
||||
|
||||
/* mode register */
|
||||
#define MOD_RM 0x01
|
||||
|
@ -306,6 +306,7 @@ static int el3_isa_match(struct device *pdev, unsigned int ndev)
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
SET_NETDEV_DEV(dev, pdev);
|
||||
netdev_boot_setup_check(dev);
|
||||
|
||||
if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
|
||||
@ -595,6 +596,7 @@ static int __init el3_eisa_probe (struct device *device)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(dev, device);
|
||||
netdev_boot_setup_check(dev);
|
||||
|
||||
el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
|
||||
|
@ -1690,7 +1690,7 @@ typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile __le32 * read
|
||||
skb_checksum_none_assert(new_skb);
|
||||
|
||||
if (rx->rxStatus & TYPHOON_RX_VLAN)
|
||||
__vlan_hwaccel_put_tag(new_skb,
|
||||
__vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q),
|
||||
ntohl(rx->vlanTag) & 0xffff);
|
||||
netif_receive_skb(new_skb);
|
||||
|
||||
@ -2445,9 +2445,9 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
* settings -- so we only allow the user to toggle the TX processing.
|
||||
*/
|
||||
dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
|
||||
NETIF_F_HW_VLAN_TX;
|
||||
NETIF_F_HW_VLAN_CTAG_TX;
|
||||
dev->features = dev->hw_features |
|
||||
NETIF_F_HW_VLAN_RX | NETIF_F_RXCSUM;
|
||||
NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM;
|
||||
|
||||
if(register_netdev(dev) < 0) {
|
||||
err_msg = "unable to register netdev";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user