* [PATCH] move ip[6]_queue rerouting up to nf_queue
From: Harald Welte @ 2005-07-26 16:45 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1.1: Type: text/plain, Size: 496 bytes --]
Hi Dave,
please apply. If you dislike the yet-another-NPROTO nature of it, I'll
prepare one with kmalloc-at-startup.
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #1.2: 12-queue-reroute.patch --]
[-- Type: text/plain, Size: 13365 bytes --]
Move reroute-after-queue code from ip_queue and ip6_queue up to the
nf_queue layer. The rerouting functionality is required by the core,
therefore it has to be implemented by the core and not in individual queue
handlers.
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit f61a93c9b4b59f82ef781a50ec955c50fe32e5db
tree 588cbbf9f57d64c4be73314b9ddbbfdc6932ec55
parent 93bd54e21ddb8f5553fd8a8beaae7803ff4e3852
author laforge <laforge@netfilter.org> Di, 26 Jul 2005 18:42:37 +0200
committer laforge <laforge@netfilter.org> Di, 26 Jul 2005 18:42:37 +0200
include/linux/netfilter.h | 11 +++++++
net/core/netfilter.c | 60 ++++++++++++++++++++++++++++++--------
net/ipv4/netfilter.c | 64 +++++++++++++++++++++++++++++++++++++++-
net/ipv4/netfilter/ip_queue.c | 27 -----------------
net/ipv6/af_inet6.c | 6 ++++
net/ipv6/netfilter.c | 62 +++++++++++++++++++++++++++++++++++++++
net/ipv6/netfilter/ip6_queue.c | 24 ---------------
7 files changed, 189 insertions(+), 65 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -198,6 +198,17 @@ extern void nf_invalidate_cache(int pf);
Returns true or false. */
extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
+struct nf_queue_rerouter {
+ void (*save)(const struct sk_buff *skb, struct nf_info *info);
+ int (*reroute)(struct sk_buff **skb, const struct nf_info *info);
+ int rer_size;
+};
+
+#define nf_info_reroute(x) ((void *)x + sizeof(typeof(x)))
+
+extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer);
+extern int nf_unregister_queue_rerouter(int pf);
+
#else /* !CONFIG_NETFILTER */
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
diff --git a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -53,6 +53,9 @@ static struct nf_queue_handler_t {
nf_queue_outfn_t outfn;
void *data;
} queue_handler[NPROTO];
+
+static struct nf_queue_rerouter queue_rerouter[NPROTO];
+
static DEFINE_RWLOCK(queue_handler_lock);
int nf_register_hook(struct nf_hook_ops *reg)
@@ -260,11 +263,34 @@ int nf_unregister_queue_handler(int pf)
return 0;
}
+int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer)
+{
+ if (pf >= NPROTO)
+ return -EINVAL;
+
+ write_lock_bh(&queue_handler_lock);
+ memcpy(&queue_rerouter[pf], rer, sizeof(queue_rerouter[pf]));
+ write_unlock_bh(&queue_handler_lock);
+
+ return 0;
+}
+
+int nf_unregister_queue_rerouter(int pf)
+{
+ if (pf >= NPROTO)
+ return -EINVAL;
+
+ write_lock_bh(&queue_handler_lock);
+ memset(&queue_rerouter[pf], 0, sizeof(queue_rerouter[pf]));
+ write_unlock_bh(&queue_handler_lock);
+ return 0;
+}
+
/*
* Any packet that leaves via this function must come back
* through nf_reinject().
*/
-static int nf_queue(struct sk_buff *skb,
+static int nf_queue(struct sk_buff **skb,
struct list_head *elem,
int pf, unsigned int hook,
struct net_device *indev,
@@ -282,17 +308,17 @@ static int nf_queue(struct sk_buff *skb,
read_lock(&queue_handler_lock);
if (!queue_handler[pf].outfn) {
read_unlock(&queue_handler_lock);
- kfree_skb(skb);
+ kfree_skb(*skb);
return 1;
}
- info = kmalloc(sizeof(*info), GFP_ATOMIC);
+ info = kmalloc(sizeof(*info)+queue_rerouter[pf].rer_size, GFP_ATOMIC);
if (!info) {
if (net_ratelimit())
printk(KERN_ERR "OOM queueing packet %p\n",
- skb);
+ *skb);
read_unlock(&queue_handler_lock);
- kfree_skb(skb);
+ kfree_skb(*skb);
return 1;
}
@@ -311,15 +337,21 @@ static int nf_queue(struct sk_buff *skb,
if (outdev) dev_hold(outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
- if (skb->nf_bridge) {
- physindev = skb->nf_bridge->physindev;
+ if ((*skb)->nf_bridge) {
+ physindev = (*skb)->nf_bridge->physindev;
if (physindev) dev_hold(physindev);
- physoutdev = skb->nf_bridge->physoutdev;
+ physoutdev = (*skb)->nf_bridge->physoutdev;
if (physoutdev) dev_hold(physoutdev);
}
#endif
+ if (queue_rerouter[pf].save)
+ queue_rerouter[pf].save(*skb, info);
+
+ status = queue_handler[pf].outfn(*skb, info, queue_handler[pf].data);
+
+ if (status >= 0 && queue_rerouter[pf].reroute)
+ status = queue_rerouter[pf].reroute(skb, info);
- status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
read_unlock(&queue_handler_lock);
if (status < 0) {
@@ -332,9 +364,11 @@ static int nf_queue(struct sk_buff *skb,
#endif
module_put(info->elem->owner);
kfree(info);
- kfree_skb(skb);
+ kfree_skb(*skb);
+
return 1;
}
+
return 1;
}
@@ -365,7 +399,7 @@ next_hook:
ret = -EPERM;
} else if (verdict == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
- if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn))
+ if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn))
goto next_hook;
}
unlock:
@@ -428,7 +462,7 @@ void nf_reinject(struct sk_buff *skb, st
break;
case NF_QUEUE:
- if (!nf_queue(skb, elem, info->pf, info->hook,
+ if (!nf_queue(&skb, elem, info->pf, info->hook,
info->indev, info->outdev, info->okfn))
goto next_hook;
break;
@@ -573,4 +607,6 @@ EXPORT_SYMBOL(nf_reinject);
EXPORT_SYMBOL(nf_setsockopt);
EXPORT_SYMBOL(nf_unregister_hook);
EXPORT_SYMBOL(nf_unregister_queue_handler);
+EXPORT_SYMBOL_GPL(nf_register_queue_rerouter);
+EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter);
EXPORT_SYMBOL(nf_unregister_sockopt);
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -1,10 +1,11 @@
-#include <linux/config.h>
+/* IPv4 specific functions of netfilter core */
+#include <linux/config.h>
#ifdef CONFIG_NETFILTER
-/* IPv4 specific functions of netfilter core */
#include <linux/kernel.h>
#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -76,4 +77,63 @@ int ip_route_me_harder(struct sk_buff **
return 0;
}
EXPORT_SYMBOL(ip_route_me_harder);
+
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip_rt_info {
+ u_int32_t daddr;
+ u_int32_t saddr;
+ u_int8_t tos;
+};
+
+static void queue_save(const struct sk_buff *skb, struct nf_info *info)
+{
+ struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP_LOCAL_OUT) {
+ const struct iphdr *iph = skb->nh.iph;
+
+ rt_info->tos = iph->tos;
+ rt_info->daddr = iph->daddr;
+ rt_info->saddr = iph->saddr;
+ }
+}
+
+static int queue_reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+ const struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP_LOCAL_OUT) {
+ struct iphdr *iph = (*pskb)->nh.iph;
+
+ if (!(iph->tos == rt_info->tos
+ && iph->daddr == rt_info->daddr
+ && iph->saddr == rt_info->saddr))
+ return ip_route_me_harder(pskb);
+ }
+ return 0;
+}
+
+static struct nf_queue_rerouter ip_reroute = {
+ .rer_size = sizeof(struct ip_rt_info),
+ .save = queue_save,
+ .reroute = queue_reroute,
+};
+
+static int init(void)
+{
+ return nf_register_queue_rerouter(PF_INET, &ip_reroute);
+}
+
+static void fini(void)
+{
+ nf_unregister_queue_rerouter(PF_INET);
+}
+
+module_init(init);
+module_exit(fini);
+
#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -43,17 +43,10 @@
#define NET_IPQ_QMAX 2088
#define NET_IPQ_QMAX_NAME "ip_queue_maxlen"
-struct ipq_rt_info {
- __u8 tos;
- __u32 daddr;
- __u32 saddr;
-};
-
struct ipq_queue_entry {
struct list_head list;
struct nf_info *info;
struct sk_buff *skb;
- struct ipq_rt_info rt_info;
};
typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -299,14 +292,6 @@ ipq_enqueue_packet(struct sk_buff *skb,
entry->info = info;
entry->skb = skb;
- if (entry->info->hook == NF_IP_LOCAL_OUT) {
- struct iphdr *iph = skb->nh.iph;
-
- entry->rt_info.tos = iph->tos;
- entry->rt_info.daddr = iph->daddr;
- entry->rt_info.saddr = iph->saddr;
- }
-
nskb = ipq_build_packet_message(entry, &status);
if (nskb == NULL)
goto err_out_free;
@@ -386,18 +371,6 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, st
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
- /*
- * Extra routing may needed on local out, as the QUEUE target never
- * returns control to the table.
- */
- if (e->info->hook == NF_IP_LOCAL_OUT) {
- struct iphdr *iph = e->skb->nh.iph;
-
- if (!(iph->tos == e->rt_info.tos
- && iph->daddr == e->rt_info.daddr
- && iph->saddr == e->rt_info.saddr))
- return ip_route_me_harder(&e->skb);
- }
return 0;
}
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -757,6 +757,9 @@ static int __init inet6_init(void)
err = igmp6_init(&inet6_family_ops);
if (err)
goto igmp_fail;
+ err = ipv6_netfilter_init();
+ if (err)
+ goto netfilter_fail;
/* Create /proc/foo6 entries. */
#ifdef CONFIG_PROC_FS
err = -ENOMEM;
@@ -813,6 +816,8 @@ proc_tcp6_fail:
raw6_proc_exit();
proc_raw6_fail:
#endif
+ ipv6_netfilter_fini();
+netfilter_fail:
igmp6_cleanup();
igmp_fail:
ndisc_cleanup();
@@ -852,6 +857,7 @@ static void __exit inet6_exit(void)
ip6_route_cleanup();
ipv6_packet_cleanup();
igmp6_cleanup();
+ ipv6_netfilter_fini();
ndisc_cleanup();
icmpv6_cleanup();
#ifdef CONFIG_SYSCTL
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -4,6 +4,8 @@
#include <linux/kernel.h>
#include <linux/ipv6.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
#include <net/dst.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
@@ -39,4 +41,64 @@ int ip6_route_me_harder(struct sk_buff *
}
EXPORT_SYMBOL(ip6_route_me_harder);
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip6_rt_info {
+ struct in6_addr daddr;
+ struct in6_addr saddr;
+};
+
+static void save(const struct sk_buff *skb, struct nf_info *info)
+{
+ struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP6_LOCAL_OUT) {
+ struct ipv6hdr *iph = skb->nh.ipv6h;
+
+ rt_info->daddr = iph->daddr;
+ rt_info->saddr = iph->saddr;
+ }
+}
+
+static int reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+ struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP6_LOCAL_OUT) {
+ struct ipv6hdr *iph = (*pskb)->nh.ipv6h;
+ if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
+ !ipv6_addr_equal(&iph->saddr, &rt_info->saddr))
+ return ip6_route_me_harder(*pskb);
+ }
+ return 0;
+}
+
+static struct nf_queue_rerouter ip6_reroute = {
+ .rer_size = sizeof(struct ip6_rt_info),
+ .save = &save,
+ .reroute = &reroute,
+};
+
+int __init ipv6_netfilter_init(void)
+{
+ return nf_register_queue_rerouter(PF_INET6, &ip6_reroute);
+}
+
+void __exit ipv6_netfilter_fini(void)
+{
+ nf_unregister_queue_rerouter(PF_INET6);
+}
+
+#else /* CONFIG_NETFILTER */
+int __init ipv6_netfilter_init(void)
+{
+ return 0;
+}
+
+void __exit ipv6_netfilter_fini(void)
+{
+}
#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -47,16 +47,10 @@
#define NET_IPQ_QMAX 2088
#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen"
-struct ipq_rt_info {
- struct in6_addr daddr;
- struct in6_addr saddr;
-};
-
struct ipq_queue_entry {
struct list_head list;
struct nf_info *info;
struct sk_buff *skb;
- struct ipq_rt_info rt_info;
};
typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -296,13 +290,6 @@ ipq_enqueue_packet(struct sk_buff *skb,
entry->info = info;
entry->skb = skb;
- if (entry->info->hook == NF_IP_LOCAL_OUT) {
- struct ipv6hdr *iph = skb->nh.ipv6h;
-
- entry->rt_info.daddr = iph->daddr;
- entry->rt_info.saddr = iph->saddr;
- }
-
nskb = ipq_build_packet_message(entry, &status);
if (nskb == NULL)
goto err_out_free;
@@ -382,17 +369,6 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, st
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
- /*
- * Extra routing may needed on local out, as the QUEUE target never
- * returns control to the table.
- * Not a nice way to cmp, but works
- */
- if (e->info->hook == NF_IP_LOCAL_OUT) {
- struct ipv6hdr *iph = e->skb->nh.ipv6h;
- if (!ipv6_addr_equal(&iph->daddr, &e->rt_info.daddr) ||
- !ipv6_addr_equal(&iph->saddr, &e->rt_info.saddr))
- return ip6_route_me_harder(e->skb);
- }
return 0;
}
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH] rename skb_ip_make_writable() to skb_make_writable()
From: Harald Welte @ 2005-07-26 15:20 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1.1: Type: text/plain, Size: 424 bytes --]
Hi Dave,
please apply to your net-2.6.14 tree:
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #1.2: 10-skb_make_writable.patch --]
[-- Type: text/plain, Size: 10547 bytes --]
Rename skb_ip_make_writable() to skb_make_writable() since there is
nothing IPv4-specific in it. In fact, it was already used by IPv6, too...
Upcoming nfnetlink_queue code will use it for any kind of packet.
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit ebf2b990ed34e768fc009057091c94b52e8aa886
tree 8302388bb8eabea3fcd375ec0234bb958818cdf2
parent 9bb3d9ecf709bb46b7f69a59b070c8365094a9be
author laforge <laforge@netfilter.org> Di, 26 Jul 2005 17:18:56 +0200
committer laforge <laforge@netfilter.org> Di, 26 Jul 2005 17:18:56 +0200
include/linux/netfilter.h | 5 +++++
include/linux/netfilter_ipv4.h | 5 -----
net/core/netfilter.c | 6 +++---
net/ipv4/netfilter/ip_nat_core.c | 4 ++--
net/ipv4/netfilter/ip_nat_helper.c | 8 ++++----
net/ipv4/netfilter/ip_nat_proto_icmp.c | 2 +-
net/ipv4/netfilter/ip_nat_proto_tcp.c | 2 +-
net/ipv4/netfilter/ip_nat_proto_udp.c | 2 +-
net/ipv4/netfilter/ip_nat_snmp_basic.c | 2 +-
net/ipv4/netfilter/ip_queue.c | 2 +-
net/ipv4/netfilter/ipt_DSCP.c | 2 +-
net/ipv4/netfilter/ipt_ECN.c | 4 ++--
net/ipv4/netfilter/ipt_TCPMSS.c | 2 +-
net/ipv4/netfilter/ipt_TOS.c | 2 +-
net/ipv6/netfilter/ip6_queue.c | 2 +-
15 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -193,6 +193,11 @@ extern void nf_ct_attach(struct sk_buff
/* FIXME: Before cache is ever used, this must be implemented for real. */
extern void nf_invalidate_cache(int pf);
+/* Call this before modifying an existing packet: ensures it is
+ modifiable and linear to the point you care about (writable_len).
+ Returns true or false. */
+extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
+
#else /* !CONFIG_NETFILTER */
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h
--- a/include/linux/netfilter_ipv4.h
+++ b/include/linux/netfilter_ipv4.h
@@ -80,11 +80,6 @@ enum nf_ip_hook_priorities {
#ifdef __KERNEL__
extern int ip_route_me_harder(struct sk_buff **pskb);
-/* Call this before modifying an existing IP packet: ensures it is
- modifiable and linear to the point you care about (writable_len).
- Returns true or false. */
-extern int skb_ip_make_writable(struct sk_buff **pskb,
- unsigned int writable_len);
#endif /*__KERNEL__*/
#endif /*__LINUX_IP_NETFILTER_H*/
diff --git a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -512,8 +512,9 @@ int ip_route_me_harder(struct sk_buff **
return 0;
}
EXPORT_SYMBOL(ip_route_me_harder);
+#endif /*CONFIG_INET*/
-int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len)
+int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len)
{
struct sk_buff *nskb;
@@ -540,8 +541,7 @@ copy_skb:
*pskb = nskb;
return 1;
}
-EXPORT_SYMBOL(skb_ip_make_writable);
-#endif /*CONFIG_INET*/
+EXPORT_SYMBOL(skb_make_writable);
/* Internal logging interface, which relies on the real
LOG target modules */
diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c
--- a/net/ipv4/netfilter/ip_nat_core.c
+++ b/net/ipv4/netfilter/ip_nat_core.c
@@ -359,7 +359,7 @@ manip_pkt(u_int16_t proto,
struct iphdr *iph;
struct ip_nat_protocol *p;
- if (!skb_ip_make_writable(pskb, iphdroff + sizeof(*iph)))
+ if (!skb_make_writable(pskb, iphdroff + sizeof(*iph)))
return 0;
iph = (void *)(*pskb)->data + iphdroff;
@@ -431,7 +431,7 @@ int icmp_reply_translation(struct sk_buf
struct ip_conntrack_tuple inner, target;
int hdrlen = (*pskb)->nh.iph->ihl * 4;
- if (!skb_ip_make_writable(pskb, hdrlen + sizeof(*inside)))
+ if (!skb_make_writable(pskb, hdrlen + sizeof(*inside)))
return 0;
inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c
--- a/net/ipv4/netfilter/ip_nat_helper.c
+++ b/net/ipv4/netfilter/ip_nat_helper.c
@@ -168,7 +168,7 @@ ip_nat_mangle_tcp_packet(struct sk_buff
struct tcphdr *tcph;
int datalen;
- if (!skb_ip_make_writable(pskb, (*pskb)->len))
+ if (!skb_make_writable(pskb, (*pskb)->len))
return 0;
if (rep_len > match_len
@@ -228,7 +228,7 @@ ip_nat_mangle_udp_packet(struct sk_buff
match_offset + match_len)
return 0;
- if (!skb_ip_make_writable(pskb, (*pskb)->len))
+ if (!skb_make_writable(pskb, (*pskb)->len))
return 0;
if (rep_len > match_len
@@ -315,7 +315,7 @@ ip_nat_sack_adjust(struct sk_buff **pskb
optoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr);
optend = (*pskb)->nh.iph->ihl*4 + tcph->doff*4;
- if (!skb_ip_make_writable(pskb, optend))
+ if (!skb_make_writable(pskb, optend))
return 0;
dir = CTINFO2DIR(ctinfo);
@@ -363,7 +363,7 @@ ip_nat_seq_adjust(struct sk_buff **pskb,
this_way = &ct->nat.info.seq[dir];
other_way = &ct->nat.info.seq[!dir];
- if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
+ if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
return 0;
tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
diff --git a/net/ipv4/netfilter/ip_nat_proto_icmp.c b/net/ipv4/netfilter/ip_nat_proto_icmp.c
--- a/net/ipv4/netfilter/ip_nat_proto_icmp.c
+++ b/net/ipv4/netfilter/ip_nat_proto_icmp.c
@@ -62,7 +62,7 @@ icmp_manip_pkt(struct sk_buff **pskb,
struct icmphdr *hdr;
unsigned int hdroff = iphdroff + iph->ihl*4;
- if (!skb_ip_make_writable(pskb, hdroff + sizeof(*hdr)))
+ if (!skb_make_writable(pskb, hdroff + sizeof(*hdr)))
return 0;
hdr = (struct icmphdr *)((*pskb)->data + hdroff);
diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c
--- a/net/ipv4/netfilter/ip_nat_proto_tcp.c
+++ b/net/ipv4/netfilter/ip_nat_proto_tcp.c
@@ -103,7 +103,7 @@ tcp_manip_pkt(struct sk_buff **pskb,
if ((*pskb)->len >= hdroff + sizeof(struct tcphdr))
hdrsize = sizeof(struct tcphdr);
- if (!skb_ip_make_writable(pskb, hdroff + hdrsize))
+ if (!skb_make_writable(pskb, hdroff + hdrsize))
return 0;
iph = (struct iphdr *)((*pskb)->data + iphdroff);
diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c
--- a/net/ipv4/netfilter/ip_nat_proto_udp.c
+++ b/net/ipv4/netfilter/ip_nat_proto_udp.c
@@ -94,7 +94,7 @@ udp_manip_pkt(struct sk_buff **pskb,
u32 oldip, newip;
u16 *portptr, newport;
- if (!skb_ip_make_writable(pskb, hdroff + sizeof(*hdr)))
+ if (!skb_make_writable(pskb, hdroff + sizeof(*hdr)))
return 0;
iph = (struct iphdr *)((*pskb)->data + iphdroff);
diff --git a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c
--- a/net/ipv4/netfilter/ip_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/ip_nat_snmp_basic.c
@@ -1275,7 +1275,7 @@ static int help(struct sk_buff **pskb,
return NF_DROP;
}
- if (!skb_ip_make_writable(pskb, (*pskb)->len))
+ if (!skb_make_writable(pskb, (*pskb)->len))
return NF_DROP;
spin_lock_bh(&snmp_lock);
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -382,7 +382,7 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, st
}
skb_put(e->skb, diff);
}
- if (!skb_ip_make_writable(&e->skb, v->data_len))
+ if (!skb_make_writable(&e->skb, v->data_len))
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
diff --git a/net/ipv4/netfilter/ipt_DSCP.c b/net/ipv4/netfilter/ipt_DSCP.c
--- a/net/ipv4/netfilter/ipt_DSCP.c
+++ b/net/ipv4/netfilter/ipt_DSCP.c
@@ -39,7 +39,7 @@ target(struct sk_buff **pskb,
if (((*pskb)->nh.iph->tos & IPT_DSCP_MASK) != sh_dscp) {
u_int16_t diffs[2];
- if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
+ if (!skb_make_writable(pskb, sizeof(struct iphdr)))
return NF_DROP;
diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c
--- a/net/ipv4/netfilter/ipt_ECN.c
+++ b/net/ipv4/netfilter/ipt_ECN.c
@@ -31,7 +31,7 @@ set_ect_ip(struct sk_buff **pskb, const
!= (einfo->ip_ect & IPT_ECN_IP_MASK)) {
u_int16_t diffs[2];
- if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
+ if (!skb_make_writable(pskb, sizeof(struct iphdr)))
return 0;
diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
@@ -66,7 +66,7 @@ set_ect_tcp(struct sk_buff **pskb, const
|| tcph->cwr == einfo->proto.tcp.cwr)))
return 1;
- if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
+ if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
return 0;
tcph = (void *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl*4;
diff --git a/net/ipv4/netfilter/ipt_TCPMSS.c b/net/ipv4/netfilter/ipt_TCPMSS.c
--- a/net/ipv4/netfilter/ipt_TCPMSS.c
+++ b/net/ipv4/netfilter/ipt_TCPMSS.c
@@ -58,7 +58,7 @@ ipt_tcpmss_target(struct sk_buff **pskb,
unsigned int i;
u_int8_t *opt;
- if (!skb_ip_make_writable(pskb, (*pskb)->len))
+ if (!skb_make_writable(pskb, (*pskb)->len))
return NF_DROP;
iph = (*pskb)->nh.iph;
diff --git a/net/ipv4/netfilter/ipt_TOS.c b/net/ipv4/netfilter/ipt_TOS.c
--- a/net/ipv4/netfilter/ipt_TOS.c
+++ b/net/ipv4/netfilter/ipt_TOS.c
@@ -33,7 +33,7 @@ target(struct sk_buff **pskb,
if (((*pskb)->nh.iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
u_int16_t diffs[2];
- if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
+ if (!skb_make_writable(pskb, sizeof(struct iphdr)))
return NF_DROP;
diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -378,7 +378,7 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, st
}
skb_put(e->skb, diff);
}
- if (!skb_ip_make_writable(&e->skb, v->data_len))
+ if (!skb_make_writable(&e->skb, v->data_len))
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: Linux Policy Routing-Based IDS Load Balancer HOWTO
From: P @ 2005-07-26 15:09 UTC (permalink / raw)
To: jeremy.guthrie; +Cc: netdev
In-Reply-To: <200507260946.38894.jeremy.guthrie@berbee.com>
Jeremy M. Guthrie wrote:
> Linux Policy Routing-Based IDS Load Balancer HOWTO
great thanks!
> 4.2.3.1 CPUs should only following one NIC.
>
> If you look at the output below you can see that CPU0 is taking the
> interrupts for eth3. CPU1 is taking interrupts for eth2 & eth0. Optimising
> any system relies on keep thrashing to a minimum.
I recommend you rename these interfaces throughout the doc,
and then you can refer to and write generic scripts against these
known names. For example:
ip link set dev eth2 name ids1
ip link set dev eth3 name ids2
> As a result I highly
> recommend disable IRQ Balancing.
>
> make menuconfig for your kernel config
> Select "Processor type and features"
> Disable "Enable kernel irq balancing"
> Rebuild your kernel and reboot.
>
> You will have to poke around /proc to set which CPU an interrupt binds to.
> Here is what was used to set the interrupt/CPU bindings down below:
> echo 01 > /proc/irq/18/smp_affinity
> echo 02 > /proc/irq/20/smp_affinity
>
> The value used is expressed in powers of two. ie. CPU3 would actually be
> 04.
>
> cat /proc/interrupts
> CPU0 CPU1
> 0: 3184569581 1789102599 IO-APIC-edge timer
> 1: 1005 218 IO-APIC-edge i8042
> 7: 0 0 IO-APIC-level ohci_hcd
> 8: 1 1 IO-APIC-edge rtc
> 12: 122 74 IO-APIC-edge i8042
> 14: 2 0 IO-APIC-edge ide0
> 18: 995373697 5139 IO-APIC-level eth3
> 20: 2 1378253801 IO-APIC-level eth2
> 27: 7542100 9352305 IO-APIC-level eth0
> 28: 4150402 13187680 IO-APIC-level aic7xxx
> 30: 0 0 IO-APIC-level acpi
> NMI: 0 0
> LOC: 679927478 679903506
> ERR: 0
> MIS: 0
As an example of a generic script:
for iface in ids1 ids2; do
int=`grep $iface\$ /proc/interrupts | cut -d: -f1`
int=`echo $int` #strip whitespace
[ "$iface" = "ids1" ] && mask=01 || mask=04
echo $mask > /proc/irq/$int/smp_affinity
done
> 4.3.3 Basic scripts
I find "watch" very useful, for example:
watch -n1 `ethtool -S ids1`
--
Pádraig Brady - http://www.pixelbeat.org
--
^ permalink raw reply
* [2.6 patch] fix ip_conntrack_put prototype
From: Adrian Bunk @ 2005-07-26 14:56 UTC (permalink / raw)
To: netfilter-devel; +Cc: netdev, linux-kernel
The function is not inline.
Signed-off-by: Adrian Bunk <bunk@stusta.de>
--- linux-2.6.13-rc3-mm1-full/include/linux/netfilter_ipv4/ip_conntrack.h.old 2005-07-26 13:44:20.000000000 +0200
+++ linux-2.6.13-rc3-mm1-full/include/linux/netfilter_ipv4/ip_conntrack.h 2005-07-26 13:45:41.000000000 +0200
@@ -236,7 +236,7 @@
}
/* decrement reference count on a conntrack */
-extern inline void ip_conntrack_put(struct ip_conntrack *ct);
+extern void ip_conntrack_put(struct ip_conntrack *ct);
/* call to create an explicit dependency on ip_conntrack. */
extern void need_ip_conntrack(void);
^ permalink raw reply
* Linux Policy Routing-Based IDS Load Balancer HOWTO
From: Jeremy M. Guthrie @ 2005-07-26 14:46 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 55962 bytes --]
Linux Policy Routing-Based IDS Load Balancer HOWTO
What you say? Well, some of you may have remember in early January of this
year I was spamming the list pretty hard trying to get a host to route quite
a bit of data. Turns out it was for an Linux based IDS load balancer. I am
80-90% done and I'd like to get your feed back as to how bad I've horked
this. Specifically are there descriptions for things like gc_interval and
the like that are so technically wrong I should be slapped with a wet
penguin?
Obvious things I have not completed yet:
1. spell checker, that will come after the list has beaten this up
2. formatting, I gave up on general formatting till the very end
3. the TOC isn't finished yet
If you could please, review the tuning/performance enhancing sections.
BTW, my first howto....
ids-load-balancing-HOWTO
Jeremy M. Guthrie
jeremy.guthrie@berbee.com
February 2004
Version 1.0
Jeremy M. Guthrie
2-5-2005
-----------------------------------------------------------------------------
Table of Contents
0. Credit and Notes About the Author
1. Terms
1. The Example Problem
2. Possible Solutions
3. The Linux Answer
3.1. Further Example Details
3.2. Needed Software Updates & Other Requirements
3.3. Notes About Limitations
3.4. It's All About the 'IP'
3.5. Other assumptions
3.6. L2CC vs EBTables & L2-NAT
3.6.1 L2CC Config
3.6.2 EBTables & L2-NAT
3.7. The Policy Router Config
4. Performance Tuning
5. Copyright
5.1. GNU Free Documentation License
5.2. PREAMBLE
5.3. APPLICABILITY AND DEFINITIONS
5.4. VERBATIM COPYING
5.5. COPYING IN QUANTITY
5.6. MODIFICATIONS
5.7. COMBINING DOCUMENTS
5.8. COLLECTIONS OF DOCUMENTS
5.9. AGGREGATION WITH INDEPENDENT WORKS
5.10. TRANSLATION
5.11. TERMINATION
5.12. FUTURE REVISIONS OF THIS LICENSE
5.13. How to use this License for your documents
-----------------------------------------------------------------------------
0. Credit and Notes About the Author
First and foremost this document would not be possible if it were not for
Jim Leu, Robert Olsson, Stephen Hemminger, David Miller, Jesse Brandeburg,
Chris Gerg, and Jon Vanderhill. All of these people were instrumental in
providing code, feedback, resources, or other critical input to help get this
document and system where it is today.
I myself am a network engineer. I have been building or managing networks
since 1992. I have been a Linux advocate since late 1995. It is my primary
OS for what I do. I am not a kernel developer though I have written other
software packages. I make no claim that I am the final authority on systems
I reference within. I am providing descriptions of the bits and pieces based
on existing documentation or discussions I have had with other persons. IOW,
if you see a blatant error or a feature omission, it is not on purpose and
would love the feedback to correct this documentation.
-----------------------------------------------------------------------------
1. Terms
EBTables: A Layer-2 filtering technology in the Linux V2.6 kernel.
Linear Search: The process of one-by-one searching through the hash
collision buckets.
L2CC: Layer Two Cross Connect: A software patch created by Jim Leu that
takes all input frames from an ethernet controller and NATs the
destination MAC address to be that of some host on an outbound
port.
L2XC: Another name for the L2CC software.
Innovation: "the introduction of something new" as defined by
Merriam-Webster Online(www.m-w.com).
MAC-Munger: Another name for the L2CC software.
Policy-Router: A router which routes because of a dictated policy rather
than by normal IPV4 per-hop-behavior. ie. route all port 80
out my DSL link, all SMTP traffic out my cable link vs. default
route traffic via my cable.
SPAN: Switch port analyzer. Think of it as a port which mirrors traffic
off of a VLAN or switchport out another port. ie. mirror all
all traffic going in/out of the Firewall's inside interface to the
SPAN port.
-----------------------------------------------------------------------------
1. The Example Problem
You are a large business with a lot of Internet bandwidth. Your IDS is
capable of dealing with some subset of your traffic volume but it cannot
handle it by itself. It also would cost too much money to buy a bigger,
faster, IDS. You want to buy the same model as you already have. The two
IDSes together can handle the volume but now you have to figure out how to
divide the traffic up between the two.
Sounds simple doesn't it?
Example bandwidth, 800 megabit per second. Assume you are using Cisco IDS
4255 where each IDS lists at $25,000 a piece. A 4255 is supposed to be
capable of 600mbps. Remember, you want the IDS to see all of the data in
a flow, not just one half! So you cannot just break up the flow without
some thought.
-----------------------------------------------------------------------------
2. Possible Solutions
F5, Radware, and Top Layer networks all make boxes that will split data
appart. All of these are commercial products that help load balance
flows for IDSs or traffic management. They are not cheap and in some cases
are budget busters.
The example network hardware I reference in here will be Cisco platforms
as that is what I am most familiar with.
-----------------------------------------------------------------------------
3. The Linux Answer
The good news is that Linux has an answer with L2CC/EBTables and Policy
Routing. The two main components run in the kernel so they operate securely.
I will detail scaling the solution upward as it should be able to meet the
needs of most any bandwidth.
The Linux solution identified herein has been dubbed by Chris Gerg as the
(I(DS)^2). However I will to the whole system as the IDS load balancer here
after.
The concept is simple:
(or EBTables)
+---------------+ +---+
| Catalyst 6509 |-[1000SX-SPAN]->| l | +---------------+
+---------------+ | 2 |-[1000SX]->| Policy Router |
+---------------+ | c | +-+-----+-------+
| Catalyst 6509 |-[1000SX-SPAN]->| c | |
+---------------+ +---+ |[1000SX]
v
+-------------------+
| Catalyst 3750 Gig |
+---+------------+--+
| |
|<--1000TX-->|
+---+---+ |
| IDS A | |
+-------+ |
+---+---+
| IDS A |
+-------+
There is a highly available network with two enterprise class switches
front-ending the network. Each switch hands off a SPAN port to the Layer Two
Cross Connect(l2cc/l2xc) host AKA EBTables Host. The SPAN data is an
identical copy of the data from the ports they mirror.
If the destination MAC address of an example packet is 05:05:05:03:03:03,
then we to NAT this to an address of the policy router's outside interface
MAC address. Why? Normal routers will not route data unless the layer two
destination address matches their own MAC address. The l2cc host
changes/L2-NATs the destination MAC address to be that of the Policy Router's
'outside' interface. When the data arrives at the policy router, it looks in
its ip routing ruleset to determine what 'table' to use to forward the
incoming packets. Once a table is found and selected, that table is then
used to define the per-hop-behavior at that point in time for that packet.
3.1. Further Example Details
Time to add yet another layer of detail to the example. We will assume we
want to run Snort or NTOP against all traffic coming through our network. In
this example we will assume that we have 1.0.0.0/16 assigned to us. We also
know that we have a pretty good distribution of traffic such that 1.0.0.0/17
gets about 350mbps and 1.0.128.0/17 gets about 450 mbps.
What we also know is that because of the network that traffic to/from
both /17 subnets comes in over both SPANs but is NOT duplicate traffic. This
can because of things like per-packet or per-flow routing decisions made by
downstream equipment.
3.2. Needed Software Updates & Other Requirements
You will want to make sure you have the latest tools for the job. I have
only ever worked this solution using Intel Gig NICs. Others should work but
I have never tested them.
Software to get:
1. Latest Intel E1000 drivers
http://sourceforge.net/projects/e1000/
2. Latest IPRoute2 utilities:
http://www.policyrouting.org/
3. Latest L2CC software:
http://mpls-linux.sf.net/
4. A V2.4 & V2.6 Linux Kernel:
http://www.kernel.org/
L2CC requires V2.4 and you should use V2.6 for your policy router.
5. Schedutils
http://tech9.net/rml/schedutils/
6. EBTables
http://ebtables.sourceforge.net/
As for other requirements....
I HIGHLY recommend a multi-CPU box. Hyperthread has shown advantages and
works well in our implementation. I typically assign one CPU per NIC on
either the EBTables box and/or Policy Router. Your mileage will vary but use
common sense. If you have a dual 3.2 ghz P4 w/ Hyperthreading then that will
have enough horse power to handle large data volumes. In some cases you
could easily assign more than one NIC to a CPU.
3.3. Notes About Limitations
Every piece of hardware or software has limits. Keep this in mind when
deploying your system. I will show you where counters are instrumented and
generically how to manage them. Like any good system administrator, you will
have to manage your system.
3.4. It's All About the 'IP'
ifconfig, netstat, and other old-style utilities are being phased out
slowly. Familiarize yourself with the 'ip' utility from the iproute2 package
as it replaces the prior listed programs. The iproute2 package includes
other programs that will help in providing access to critical performance
information. 'lnstat' and 'rtstat' can be used to poll routing performance
information from your kernel DEPENDING on which kernel release you are
running.
3.5. Other assumptions
Going forward it will be assumed that you have the appropriate kernels
and/or features installed unless we talk explicitly about a feature.
3.6 L2CC vs EBTables & L2-NAT
L2CC was the only option available when this document was first written but
now EBTables is available. EBTables was beaten up a quite bit in the May
2005 Networld+Interop in Vegas. EBTables proved to be a very reliable bit of
software. L2CC has more burn in time for my organization but we are in the
process of migrating. This document will provide examples for both L2CC &
EBTables.
3.6.1 L2CC Config
The example L2CC host has three Gig NICs. eth0-1 are gathering SPAN data
while eth2 is the output port. The Policy router's eth0 MAC address is
01:01:01:10:10:10.
The L2CC host will need the L2CC patch applied against the V2.4 kernel.
From there do the following:
make menuconfig
Select "Networking Options"
Compile in "Layer 2 Cross Connectr (EXPERIMENTAL)", do not build as a
module.
Rebuild your kernel and reboot.
#add two entries, one for each NIC
l2cc -a -i eth0 -o eth2 -m 01:01:01:10:10:10
l2cc -a -i eth1 -o eth2 -m 01:01:01:10:10:10
#delete two entries, one for each NIC
l2cc -d -i eth0 -o eth2 -m 01:01:01:10:10:10
l2cc -d -i eth1 -o eth2 -m 01:01:01:10:10:10
*WARNING* Test that your configuration is working using TCPDump in an
ISOLATED network.
In the above example, I should be able to run three instances of TCPDump
and see that data coming in eth0 & eth1 is having its MAC address NAT'd when
being transmitted out eth2.
3.6.2 EBTables & L2-NAT
The example EBTables host has three Gig NICs. eth0-1 are gathering SPAN
data while eth2 is the output port. The Policy router's eth0 MAC address is
01:01:01:10:10:10.
EBTables will require a Linux host running with a V2.6 kernel.
To build your Linux kernel with EBTables support:
make menuconfig
Select "Device Drivers"
Select "Networking Support"
Select "Networking Options"
Compile in "802.1d Ethernet Bridging"
Select "Network packet filtering"
Select "Bridge: Netfilter Configuration"
Select "Ethernet Bridge tables (ebtables) support"
Select "ebt: nat table support"
Select "ebt: dnat target support"
Select "ebt: snat target support"
Rebuild your kernel and reboot.
#prep your interfaces
ifconfig eth0 up
ifconfig eth1 up
ifconfig eth2 up
#create the br0 interface
brctl addbr br0
#turn off spanning-tree
brctl stp br0 off
#add interfaces to the br0 broadcast domain
brctl addif br0 eth0
brctl addif br0 eth1
brctl addif br0 eth2
#Prep ebtables
ebtables -F INPUT
ebtables -F OUTPUT
ebtables -F FORWARD
ebtables -t nat -F PREROUTING
#NAT all incoming data on eth0 to 00:11:25:8c:8c:37
ebtables -t nat -A PREROUTING -I eth0 -j dnat -to-destination
00:11:25:8c:8c:37
#NAT all incoming data on eth1 to 00:11:25:8c:8c:37
ebtables -t nat -A PREROUTING -I eth1 -j dnat -to-destination
00:11:25:8c:8c:37
#Tell EBTables to route/bridge data destined to 00:11:25:8c:8c:37 out eth2
ebtables -A OUTPUT -o eth2 -d 00:11:25:8c:8c:37 -j ACCEPT
*WARNING* Test that your configuration is working using TCPDump in an
ISOLATED network.
In the above example, I should be able to run three instances of TCPDump
and see that data coming in eth0 & eth1 is having its MAC address NAT'd when
being transmitted out eth2.
3.7. The Policy Router Config
I am going to assume you have the appropriate iproute2 package for your
kernel. You may need a newer version of the iproute2 utilities.
ie. V2.6.9 kernel
strace -f rtstat ...
--snip--
open("/proc/net/rt_cache_stat", O_RDONLY) = -1 ENOENT (No such file or
directory)
--snip--
In this case I would need the latest iproute2 code to use
the /proc/net/stat/rt_cache instead. In fact, rtstat may also be called
'lnstat'.
*ASSUMPTION* This policy router config assumes there are no overlapping
subnets. Overlapping subnets mean you must order your policy rules
appropriately for them to work. Overlapping IP examples: 192.168.0.0/16 &
192.168.0.0/24
The policy router is made up of several components. The policy router uses
rules and tables. Rules are used to classify which traffic belongs to which
table. This is where the policy in policy routing comes from. You define
what policies you want to implement.
Tables are routing tables used to device the per-hop-behavior for the
packet being routed via that table. Shortly you will see how the combination
of rules with tables are combined to split traffic.
In our example, we want to split traffic of two /17s to the two sensors.
With that in mind we will add rules to do the actual policy mapping.
Eth0 is our input device while eth1 is our output device. Eth0 will have
an IP address of 10.0.0.1/32. An IP address is required otherwise the Linux
kernel will not policy-route for the interface. Eth1 will have an IP address
of 10.0.1.1/24. Sensor 1 will have an IP address of 10.0.1.10, Sensor 2 will
have an IP address of 10.0.1.11.
Routing at high speed means any hiccup, EVEN SMALL ones, result in lost
packets as recieve rings for network cards can be overrun quickly. Thus we
have to minimize any hiccups. One hiccup is ARP. Sensors NEVER transmit and
we will always have data to send to them. You will see several changes we
will make to account for this.
#First, turn off IP forwarding before we configure routing
echo 0 > /proc/sys/net/ipv4/ip_forward
#Add static ARP entries as we should ALWAYS know what MAC
#address to associate with our Sensor IPS
arp -s 10.0.1.10 00:02:50:98:DC:1C
arp -s 10.0.1.11 00:02:50:A1:5D:5A
#send any traffic to/from 1.0.0.0/17 to table 15
ip rule add type unicast dev eth0 from 1.0.0.0/17 table 15
ip rule add type unicast dev eth0 to 1.0.0.0/17 table 15
#send any traffic to/from 1.0.128.0/17 to table 16
ip rule add type unicast dev eth0 from 1.0.128.0/17 table 16
ip rule add type unicast dev eth0 to 1.0.128.0/17 table 16
#Tell policy routing code that the only path in table 15 is via Sensor 1!
ip route add default via 10.1.0.10 dev eth1 table 15
#Tell policy routing code that the only path in table 16 is via Sensor 2!
ip route add default via 10.1.0.11 dev eth1 table 16
#When done with our changes, flush the cache
ip route flush cache
#Lastly, turn on IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
This is all you actually 'have to do' to enable a working system. There
are however other changes you SHOULD make to help with performance.
For one, if you left IP forwarding on and blew away the policy routing
table, all traffic would then follow your normal default route on the
box!!!!!!
*WARNING*
Let's complicate our policy router by adding another interface,
eth3->192.168.10.5 with a default gw to a firewall so we can remotely manage
the system. In our example, we had 800mbps heading towards our policy
router. If we turn off policy routing but left IP forwarding on, the Linux
host will try to forward >>>>800mbps<<<< of traffic towards the firewall on
the 192.168.10.0/24 network thereby killing it. Okay?! Follow? If not,
re-read till you do.
Hint: If your firewall has a 100mbps interface and you fire 800mbps at it,
the firewall will stop working because you will overload its interface with
bandwidth.
There are then two ways to protect yourself:
A) Always turn off IP forwarding before making ANY changes to the policy
router.
B) Turn on iptables filtering. Here is a quick an dirty example of an
IPtables filter to apply to this example host:
#The only data that will be allowed in or out of eth3 will be traffic
#to or from 192.168.10.5. So even if you accidentally leave IP forwarding
#on, you can trust that IP Tables it stemming the flow from burrying your
#gateway/firewall.
iptables -F
iptables -A FORWARD -s 192.168.10.5 -o eth3 -j ACCEPT
iptables -A FORWARD -o eth0 -j DROP
iptables -A OUTPUT -s 192.168.10.5 -o eth3 -j ACCEPT
iptables -A OUTPUT -o eth0 -j DROP
The switches will need some adjustments to make sure that the switch knows
exactly where the sensors are. If the sensors never transmit data on their
ports then the switches turn act as hubs which is exactly what we don't want.
#3750 config:
interface GigabitEthernet1/0/1
description Eth0 of Sensor 1
switchport access vlan 100
switchport trunk native vlan 100
switchport trunk allowed vlan none
switchport mode access
switchport nonegotiate
load-interval 30
no cdp enable
!
interface GigabitEthernet1/0/2
description Eth0 of Sensor 2
switchport access vlan 100
switchport trunk native vlan 100
switchport trunk allowed vlan none
switchport mode access
switchport nonegotiate
load-interval 30
no cdp enable
!
interface GigabitEthernet1/0/28
description Eth1 of Policy Router
switchport access vlan 100
switchport trunk native vlan 100
switchport trunk allowed vlan none
switchport mode access
switchport nonegotiate
load-interval 30
no cdp enable
!
mac-address-table static 0002.5098.DC1C vlan 100 interface
GigabitEthernet1/0/1
mac-address-table static 0002.50A1.5D5A vlan 100 interface
GigabitEthernet1/0/2
That's it? Right? Well, sure. If your system is blazing fast and does
not require any tuning. This assumes your system defaults are adequate.
That may not be the case. The rest of this document will be dedicated to
discussing eliminating the bottlenecks.
-----------------------------------------------------------------------------
4. Performance Tuning
Jumping back to a prior statement, you need to be aware of the limits of
the policy routing system. I will list them out here and we will discuss how
to go about addressing them. Some are easy to update, others are what they
are.
4.1. General Limits
Kernel Limits:
Max # of Policy Routing Rules: 32768 - some are reserved
Max # of Policy Routing Tables: 256 - some are reserved
Max # of Interfaces in V2.6 Kernel: 4096
Max # of Interfaces in V2.4 Kernel: 256
IP Route Hash Limits:
Card Limits:
Intel EtherExpress RX/TX Buffer Count: 256 packets
Max RX/TX Buffer Count: 4096 packets
4.2. Instrumentation
Knowing that any system is running well can take a bit to figure out.
There will be a few places that we concentrate on monitoring. Memory, CPU
utilization, interrupt distribution, network stack drops, network card drops,
# of routes, garbage collection, routing packets per second, # of hash
entries, and others.
4.2.1. rtstat or lnstat in the land of iproute2
Rtstat and lnstat are two tools used to watch routing activity within the
Linux kernel. Kernels prior to approximately V2.6.9 will use rtstat. After
2.6.9 iproute2 uses lnstat to gather routing detail. You can tell if your
kernel works with rtstat or lnstat by trying the existing install of rtstat.
Both tools pull data from /proc, it is a question of which one.
ie.
[plato jguthrie 10:29am]~-> rtstat
fopen: No such file or directory
4.2.2 Important /proc files for your reference.
#Data on each route cache entries/hashes
/proc/net/rt_cache
#Aggregate statistics on route cache entries/hashes
/proc/net/stat/rt_cache
#General information about the network stack on a per-cpu basis
/proc/net/softnet_stat
We will examine each of the files to examine what they can tell us.
4.2.2.1. /proc/net/softnet_stat
cat /proc/net/softnet_stat
00000130 00000034 00000000 00000000 00000000 00000000 00000000 00000000
00000000
00000150 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
The basic idea behind softnet_stat is that we use it as a way to tell us if
the Kernel itself is dropping packets. The second field in the list of nine
is the packet drop count. You can see in this example that we have dropped
0x34 packets. Look into using NAPI or other network card features to
possibly relieve CPU overhead.
4.2.3 CPUs, Memory, and Interrupts & the Rules To Follow
As with any rules here, all can be affected by your budget.
1. Assume that each CPU on your box will be handling only one NIC's
interrupts.
2. Assume that you will be using 'taskset' to keep non-kernel routing
functions assigned to a specific CPU.
3. EBTables uses little memory, you can skimp here
4. Policy Routing Chews Memory, you cannot skimp here - minimum 1 Gigabyt
of RAM ***HIGHLY*** recommended
4.2.3.1 CPUs should only following one NIC.
If you look at the output below you can see that CPU0 is taking the
interrupts for eth3. CPU1 is taking interrupts for eth2 & eth0. Optimising
any system relies on keep thrashing to a minimum. As a result I highly
recommend disable IRQ Balancing.
make menuconfig for your kernel config
Select "Processor type and features"
Disable "Enable kernel irq balancing"
Rebuild your kernel and reboot.
You will have to poke around /proc to set which CPU an interrupt binds to.
Here is what was used to set the interrupt/CPU bindings down below:
echo 01 > /proc/irq/18/smp_affinity
echo 02 > /proc/irq/20/smp_affinity
The value used is expressed in powers of two. ie. CPU3 would actually be
04.
cat /proc/interrupts
CPU0 CPU1
0: 3184569581 1789102599 IO-APIC-edge timer
1: 1005 218 IO-APIC-edge i8042
7: 0 0 IO-APIC-level ohci_hcd
8: 1 1 IO-APIC-edge rtc
12: 122 74 IO-APIC-edge i8042
14: 2 0 IO-APIC-edge ide0
18: 995373697 5139 IO-APIC-level eth3
20: 2 1378253801 IO-APIC-level eth2
27: 7542100 9352305 IO-APIC-level eth0
28: 4150402 13187680 IO-APIC-level aic7xxx
30: 0 0 IO-APIC-level acpi
NMI: 0 0
LOC: 679927478 679903506
ERR: 0
MIS: 0
4.2.3.2 Taskset is your friend.
Taskset allows an administrator to bind a software process to a specific
processor on a box. By using taskset you help cut down on CPU cache
thrashing. If your hosts will be running SNMP daemons, snort, etc, then you
will want to bind snort to the least used CPU. You want to keep your base
load balancing system predictable.
4.3 Tune your routing!
The Linux kernel defaults for routing work great in a lot of situations and
unfortunately this is not one of them. You will find that the Linux kernel
will take some tuning to get the performance you want.
The kernel counts on route hash entries to track existing conversations.
One route hash entry is used per host-host communication. ie.
Entry 1: 192.168.1.1 -> 192.168.2.2
Entry 2: 192.168.2.4 -> 192.168.7.5
Imagine HOW MANY of these you might have given your volumes of traffic.
Also imagine that the kernel has NO WAY to tell when a conversation is over.
The kernel is not following TCP/UDP conversations thus entries age out of
existence. You will need to conduct performance testing to confirm how well
your environment runs. Just be warned that the more route hash entries you
run, the more RAM the kernel WILL use.
Example 'free' from your Policy router:
/proc/sys/net/ipv4/route/gc_thresh: 786432
total used free shared buffers cached
Mem: 1034088 1007112 26976 0 310840 217220
-/+ buffers/cache: 479052 555036
Swap: 1028120 0 1028120
4.3.1 Adjust one kernel parameter and reboot.
You will need to bump up the maximum number of supported route hash entries
the kernel supports. I recommend setting this rather high and using another
parameter to set your ceiling. Add the following to your boot config kernel
parameters:
rhash_entries=2400000
4.3.2 Adjust /proc/sys/net/... to tune your routing
The Linux kernel uses six parameters to adjust how it handles managing the
routing hashes, collisions, and aging.
gc_elesticity can best be described as the average bucket depth the kernel
will accept before it starts expiring route hash entries. This will help
maintain the upper limit of active routes.
echo 8 > /proc/sys/net/ipv4/route/gc_elasticity
I had limited success playing with these next two entries seeing as I could
find little information on the effect of either one.
echo 60 > /proc/sys/net/ipv4/route/gc_interval
echo 0 > /proc/sys/net/ipv4/route/gc_min_interval
gc_thresh is another limiting factor in controlling how much RAM your
policy routing will eat up. This number cannot be greater than the
rhash_entries kernel parameter. As a rule of thumb, set your rhash_entries
parameter REALLY high(mine is 2.4million) and control your running limit with
gc_thresh.
echo 1048576 > /proc/sys/net/ipv4/route/gc_thresh
This parameter needs better kernel docs.
echo 300 > /proc/sys/net/ipv4/route/gc_timeout
The secret_interval instructs the kernel how often to blow away ALL route
hash entries regardless of how new/old they are. In our environment this is
generally bad. The CPU will be busy rebuilding thousands of entries per
second every time the cache is cleared. However we set this to run once a
day to keep memory leaks at bay(though we've never had one).
echo 86400 > /proc/sys/net/ipv4/route/secret_interval
4.3.3 Basic scripts
These aren't perfect but then again, what is....
4.3.3.1 watcherrors
#!/bin/tcsh
set interval=15
set argc=`echo $argv | wc -w | tr -s " " "\t" | cut -f2`
if ( $argc > 0 ) then
set interval=$argv[1]
endif
set stats=`ifconfig eth3 | egrep 'RX packets:' | tr -s ": " "\t" | cut -f4,6`
set interrupts=`cat /proc/interrupts | egrep "eth[23]" | tr -s " " "\t" | cut
-f 3,4`
while ( 1 )
sleep $interval
set newstats=`ifconfig eth3 | egrep 'RX packets:' | tr -s ": " "\t" |
cut -f4,6`
set packets=`expr $newstats[1] - $stats[1]`
set errors=`expr $newstats[2] - $stats[2]`
set percentage=`expr $errors "*" 10000 / $packets`
set packetspersec=`expr $packets / $interval `
set date=`date "+%m/%d/%y %H:%M:%S"`
set entries=`cat /proc/net/stat/rt_cache | tr -s " " "\t" | cut -f 1 |
head -n 2 | tail -n 1`
set newinterrupts=`cat /proc/interrupts | egrep "eth[23]" | tr -s " "
"\t" | cut -f 3,4`
set eth3int=`expr $newinterrupts[1] - $interrupts[1]`
set eth2int=`expr $newinterrupts[4] - $interrupts[4]`
echo "$date entries: $entries Pkts: $packets Err: $errors PPS:
$packetspersec Drop %: 0.$percentage% Eth3RXInt: $eth3int Eth2TXInt:
$eth2int"
set stats=( $newstats )
set interrupts=( $newinterrupts )
end
4.3.3.2 Policy Routing Control Script
This script assumes that you will have two files, a policy route file, and
policy rule file.
The ROUTE file should have the following format:
[ip of next hop] [outgoing interface] [table #]
Example 'routefile' contents:
10.0.1.10 eth2 31
10.0.1.11 eth2 32
The RULE file should have the following format:
[CIDR BLOCK] [table #]
Example 'rulefile' contents:
172.30.0.0/24 31
172.16.0.0/22 32
Data to/from 172.30.0.0/24 would be sent to sensor 10.0.1.10. Data to/from
172.16.0.0/22 would be sent to sensor 10.0.1.11.
#POLICY V1.0 script
#!/bin/tcsh
set policyrulefile=/opt/bin/policyrules
set policyroutefile=/opt/bin/policyroutes
set argc=`echo $argv | wc -w | tr -s " " "\t" | cut -f2`
if ( $argc < 3 ) then
echo Policy V1.00
echo "Usage: policy [add|delete|show] [routefile] [rulefile]"
exit
endif
set command=`echo $1 | tr "[a-z]" "[A-Z"`
if ( ( $command == "ADD" ) || ( $command == "DELETE" ) ) then
if ( ! -e $argv[2] ) then
echo Route file $argv[2] does not exist
exit
endif
if ( ! -e $argv[3] ) then
echo Rule file $argv[3] does not exist
exit
endif
endif
set policyroutefile=$argv[2]
set policyrulefile=$argv[3]
set policyrulecount=`egrep "[0-9]\/[0-9]" $policyrulefile | wc -l | tr -s " "
"\t" | cut -f2`
set policyrules=`egrep "[0-9]\/[0-9]" $policyrulefile | tr -s " " "\t"`
set policyroutecount=`egrep "[0-9] eth" $policyroutefile | wc -w | tr -s
" " "\t" | cut -f2`
set policyroutes=`egrep "[0-9] eth" $policyroutefile | tr -s " " "\t"`
if ( $command == "ADD" ) then
echo -n "Turning up..."
set alternate=0
foreach policyrule ($policyrules )
if ( $alternate ) then
set alternate=0
set table=$policyrule
# echo "Adding rule: $range $table"
echo -n "."
/sbin/ip rule add type unicast dev eth3 from $range
table $table
/sbin/ip rule add type unicast dev eth3 to $range
table $table
else
set alternate=1
set range=$policyrule
endif
end
set loop=0
while ( $loop != $policyroutecount )
set loop=`expr $loop + 1`
set gw=$policyroutes[$loop]
set loop=`expr $loop + 1`
set device=$policyroutes[$loop]
set loop=`expr $loop + 1`
set table=$policyroutes[$loop]
#echo "Adding default route: $gw $device $table"
echo -n "+"
/sbin/ip route add default via $gw dev $device table $table
end
/sbin/ip route flush cache
echo 1 > /proc/sys/net/ipv4/ip_forward
echo ""
endif
if ( $command == "DELETE" ) then
echo -n "Turning down..."
echo 0 > /proc/sys/net/ipv4/ip_forward
set alternate=0
foreach policyrule ($policyrules )
if ( $alternate ) then
set alternate=0
set table=$policyrule
#echo "Deleting rule: $range $table"
echo -n "."
/sbin/ip rule delete type unicast dev eth3 from $range
table $table
/sbin/ip rule delete type unicast dev eth3 to $range
table $table
else
set alternate=1
set range=$policyrule
endif
end
set loop=0
while ( $loop != $policyroutecount )
set loop=`expr $loop + 1`
set gw=$policyroutes[$loop]
set loop=`expr $loop + 1`
set device=$policyroutes[$loop]
set loop=`expr $loop + 1`
set table=$policyroutes[$loop]
#echo "Deleting default route: $gw $device $table"
echo -n "+"
/sbin/ip route delete default via $gw dev $device table $table
end
/sbin/ip route flush cache
echo ""
endif
if ( $command == "SHOW" ) then
set tables=`cat $policyroutefile | egrep "[0-9]" | tr -s " " "\t" |
cut -f3 | sort -u`
ip rule list
foreach table ($tables)
ip route list table $table
end
endif
-----------------------------------------------------------------------------
5. Copyright
Copyright © 2005 Jeremy M. Guthrie
Permission is granted to copy, distribute and/or modify this document under
the terms of the GNU Free Documentation License, Version 1.1 or any later
version published by the Free Software Foundation; with no Invariant
Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the license
is included in the section entitled "GNU Free Documentation License".
-----------------------------------------------------------------------------
5.1. GNU Free Documentation License
Version 1.1, March 2000
Copyright (C) 2000 Free Software Foundation, Inc. 59 Temple Place, Suite
330, Boston, MA 02111-1307 USA Everyone is permitted to copy and
distribute verbatim copies of this license document, but changing it is
not allowed.
-----------------------------------------------------------------------------
5.2. PREAMBLE
The purpose of this License is to make a manual, textbook, or other written
document "free" in the sense of freedom: to assure everyone the effective
freedom to copy and redistribute it, with or without modifying it, either
commercially or noncommercially. Secondarily, this License preserves for the
author and publisher a way to get credit for their work, while not being
considered responsible for modifications made by others.
This License is a kind of "copyleft", which means that derivative works of
the document must themselves be free in the same sense. It complements the
GNU General Public License, which is a copyleft license designed for free
software.
We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free program
should come with manuals providing the same freedoms that the software does.
But this License is not limited to software manuals; it can be used for any
textual work, regardless of subject matter or whether it is published as a
printed book. We recommend this License principally for works whose purpose
is instruction or reference.
-----------------------------------------------------------------------------
5.3. APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work that contains a notice
placed by the copyright holder saying it can be distributed under the terms
of this License. The "Document", below, refers to any such manual or work.
Any member of the public is a licensee, and is addressed as "you".
A "Modified Version" of the Document means any work containing the Document
or a portion of it, either copied verbatim, or with modifications and/or
translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of the
Document that deals exclusively with the relationship of the publishers or
authors of the Document to the Document's overall subject (or to related
matters) and contains nothing that could fall directly within that overall
subject. (For example, if the Document is in part a textbook of mathematics,
a Secondary Section may not explain any mathematics.) The relationship could
be a matter of historical connection with the subject or with related
matters, or of legal, commercial, philosophical, ethical or political
position regarding them.
The "Invariant Sections" are certain Secondary Sections whose titles are
designated, as being those of Invariant Sections, in the notice that says
that the Document is released under this License.
The "Cover Texts" are certain short passages of text that are listed, as
Front-Cover Texts or Back-Cover Texts, in the notice that says that the
Document is released under this License.
A "Transparent" copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the general
public, whose contents can be viewed and edited directly and
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or for
automatic translation to a variety of formats suitable for input to text
formatters. A copy made in an otherwise Transparent file format whose markup
has been designed to thwart or discourage subsequent modification by readers
is not Transparent. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain ASCII
without markup, Texinfo input format, LaTeX input format, SGML or XML using a
publicly available DTD, and standard-conforming simple HTML designed for
human modification. Opaque formats include PostScript, PDF, proprietary
formats that can be read and edited only by proprietary word processors, SGML
or XML for which the DTD and/or processing tools are not generally available,
and the machine-generated HTML produced by some word processors for output
purposes only.
The "Title Page" means, for a printed book, the title page itself, plus such
following pages as are needed to hold, legibly, the material this License
requires to appear in the title page. For works in formats which do not have
any title page as such, "Title Page" means the text near the most prominent
appearance of the work's title, preceding the beginning of the body of the
text.
-----------------------------------------------------------------------------
5.4. VERBATIM COPYING
You may copy and distribute the Document in any medium, either commercially
or noncommercially, provided that this License, the copyright notices, and
the license notice saying this License applies to the Document are reproduced
in all copies, and that you add no other conditions whatsoever to those of
this License. You may not use technical measures to obstruct or control the
reading or further copying of the copies you make or distribute. However, you
may accept compensation in exchange for copies. If you distribute a large
enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may
publicly display copies.
-----------------------------------------------------------------------------
5.5. COPYING IN QUANTITY
If you publish printed copies of the Document numbering more than 100, and
the Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover Texts:
Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover.
Both covers must also clearly and legibly identify you as the publisher of
these copies. The front cover must present the full title with all words of
the title equally prominent and visible. You may add other material on the
covers in addition. Copying with changes limited to the covers, as long as
they preserve the title of the Document and satisfy these conditions, can be
treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you
should put the first ones listed (as many as fit reasonably) on the actual
cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more
than 100, you must either include a machine-readable Transparent copy along
with each Opaque copy, or state in or with each Opaque copy a
publicly-accessible computer-network location containing a complete
Transparent copy of the Document, free of added material, which the general
network-using public has access to download anonymously at no charge using
public-standard network protocols. If you use the latter option, you must
take reasonably prudent steps, when you begin distribution of Opaque copies
in quantity, to ensure that this Transparent copy will remain thus accessible
at the stated location until at least one year after the last time you
distribute an Opaque copy (directly or through your agents or retailers) of
that edition to the public.
It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to give them
a chance to provide you with an updated version of the Document.
-----------------------------------------------------------------------------
5.6. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under the
conditions of sections 2 and 3 above, provided that you release the Modified
Version under precisely this License, with the Modified Version filling the
role of the Document, thus licensing distribution and modification of the
Modified Version to whoever possesses a copy of it. In addition, you must do
these things in the Modified Version:
A. Use in the Title Page (and on the covers, if any) a title distinct from
that of the Document, and from those of previous versions (which should,
if there were any, be listed in the History section of the Document). You
may use the same title as a previous version if the original publisher of
that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified Version,
together with at least five of the principal authors of the Document (all
of its principal authors, if it has less than five).
C. State on the Title page the name of the publisher of the Modified
Version, as the publisher.
D. Preserve all the copyright notices of the Document.
E. Add an appropriate copyright notice for your modifications adjacent to
the other copyright notices.
F. Include, immediately after the copyright notices, a license notice giving
the public permission to use the Modified Version under the terms of this
License, in the form shown in the Addendum below.
G. Preserve in that license notice the full lists of Invariant Sections and
required Cover Texts given in the Document's license notice.
H. Include an unaltered copy of this License.
I. Preserve the section entitled "History", and its title, and add to it an
item stating at least the title, year, new authors, and publisher of the
Modified Version as given on the Title Page. If there is no section
entitled "History" in the Document, create one stating the title, year,
authors, and publisher of the Document as given on its Title Page, then
add an item describing the Modified Version as stated in the previous
sentence.
J. Preserve the network location, if any, given in the Document for public
access to a Transparent copy of the Document, and likewise the network
locations given in the Document for previous versions it was based on.
These may be placed in the "History" section. You may omit a network
location for a work that was published at least four years before the
Document itself, or if the original publisher of the version it refers to
gives permission.
K. In any section entitled "Acknowledgements" or "Dedications", preserve the
section's title, and preserve in the section all the substance and tone
of each of the contributor acknowledgements and/or dedications given
therein.
L. Preserve all the Invariant Sections of the Document, unaltered in their
text and in their titles. Section numbers or the equivalent are not
considered part of the section titles.
M. Delete any section entitled "Endorsements". Such a section may not be
included in the Modified Version.
N. Do not retitle any existing section as "Endorsements" or to conflict in
title with any Invariant Section.
If the Modified Version includes new front-matter sections or appendices that
qualify as Secondary Sections and contain no material copied from the
Document, you may at your option designate some or all of these sections as
invariant. To do this, add their titles to the list of Invariant Sections in
the Modified Version's license notice. These titles must be distinct from any
other section titles.
You may add a section entitled "Endorsements", provided it contains nothing
but endorsements of your Modified Version by various parties--for example,
statements of peer review or that the text has been approved by an
organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list of
Cover Texts in the Modified Version. Only one passage of Front-Cover Text and
one of Back-Cover Text may be added by (or through arrangements made by) any
one entity. If the Document already includes a cover text for the same cover,
previously added by you or by arrangement made by the same entity you are
acting on behalf of, you may not add another; but you may replace the old
one, on explicit permission from the previous publisher that added the old
one.
The author(s) and publisher(s) of the Document do not by this License give
permission to use their names for publicity for or to assert or imply
endorsement of any Modified Version.
-----------------------------------------------------------------------------
5.7. COMBINING DOCUMENTS
You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified versions,
provided that you include in the combination all of the Invariant Sections of
all of the original documents, unmodified, and list them all as Invariant
Sections of your combined work in its license notice.
The combined work need only contain one copy of this License, and multiple
identical Invariant Sections may be replaced with a single copy. If there are
multiple Invariant Sections with the same name but different contents, make
the title of each such section unique by adding at the end of it, in
parentheses, the name of the original author or publisher of that section if
known, or else a unique number. Make the same adjustment to the section
titles in the list of Invariant Sections in the license notice of the
combined work.
In the combination, you must combine any sections entitled "History" in the
various original documents, forming one section entitled "History"; likewise
combine any sections entitled "Acknowledgements", and any sections entitled
"Dedications". You must delete all sections entitled "Endorsements."
-----------------------------------------------------------------------------
5.8. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this
License in the various documents with a single copy that is included in the
collection, provided that you follow the rules of this License for verbatim
copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it
individually under this License, provided you insert a copy of this License
into the extracted document, and follow this License in all other respects
regarding verbatim copying of that document.
-----------------------------------------------------------------------------
5.9. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate and
independent documents or works, in or on a volume of a storage or
distribution medium, does not as a whole count as a Modified Version of the
Document, provided no compilation copyright is claimed for the compilation.
Such a compilation is called an "aggregate", and this License does not apply
to the other self-contained works thus compiled with the Document, on account
of their being thus compiled, if they are not themselves derivative works of
the Document.
If the Cover Text requirement of section 3 is applicable to these copies of
the Document, then if the Document is less than one quarter of the entire
aggregate, the Document's Cover Texts may be placed on covers that surround
only the Document within the aggregate. Otherwise they must appear on covers
around the whole aggregate.
-----------------------------------------------------------------------------
5.10. TRANSLATION
Translation is considered a kind of modification, so you may distribute
translations of the Document under the terms of section 4. Replacing
Invariant Sections with translations requires special permission from their
copyright holders, but you may include translations of some or all Invariant
Sections in addition to the original versions of these Invariant Sections.
You may include a translation of this License provided that you also include
the original English version of this License. In case of a disagreement
between the translation and the original English version of this License, the
original English version will prevail.
-----------------------------------------------------------------------------
5.11. TERMINATION
You may not copy, modify, sublicense, or distribute the Document except as
expressly provided for under this License. Any other attempt to copy, modify,
sublicense or distribute the Document is void, and will automatically
terminate your rights under this License. However, parties who have received
copies, or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
-----------------------------------------------------------------------------
5.12. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the GNU
Free Documentation License from time to time. Such new versions will be
similar in spirit to the present version, but may differ in detail to address
new problems or concerns. See [http://www.gnu.org/copyleft/] http://
www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the
Document specifies that a particular numbered version of this License "or any
later version" applies to it, you have the option of following the terms and
conditions either of that specified version or of any later version that has
been published (not as a draft) by the Free Software Foundation. If the
Document does not specify a version number of this License, you may choose
any version ever published (not as a draft) by the Free Software Foundation.
-----------------------------------------------------------------------------
5.13. How to use this License for your documents
To use this License in a document you have written, include a copy of the
License in the document and put the following copyright and license notices
just after the title page:
Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute
and/or modify this document under the terms of the GNU Free Documentation
License, Version 1.1 or any later version published by the Free Software
Foundation; with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A
copy of the license is included in the section entitled "GNU Free
Documentation License".
If you have no Invariant Sections, write "with no Invariant Sections" instead
of saying which ones are invariant. If you have no Front-Cover Texts, write
"no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise
for Back-Cover Texts.
If your document contains nontrivial examples of program code, we recommend
releasing these examples in parallel under your choice of free software
license, such as the GNU General Public License, to permit their use in free
software.
--
--------------------------------------------------
Jeremy M. Guthrie jeremy.guthrie@berbee.com
Senior Network Engineer Phone: 608-298-1061
Berbee Fax: 608-288-3007
5520 Research Park Drive NOC: 608-298-1102
Madison, WI 53711
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH 2.6.12.2] XFRM: BEET IPsec mode for Linux
From: Miika Komu @ 2005-07-26 13:02 UTC (permalink / raw)
To: netdev
In-Reply-To: <1122295307.14873.37.camel@odysse>
> Ericsson has already developed a BEET patch for *BSD. Our patch
> provides the similar functionality, but using the XFRM architecture.
> The patch is included at the end of this email and also at the following
> URL:
> http://hipl.hiit.fi/beet/beet-patch-v1.0-2.6.12.2
We added more files to the same directory that are helpful for testing the
patch. There is a patch for setkey which enables you to configure security
associations manually. Also, there is a simple script that can be used for
quick testing of ping over BEET ESP security associations.
http://hipl.hiit.fi/beet/ipsec-tools-0.5.1-patch-beet
http://hipl.hiit.fi/beet/run-beet.sh
Looking forward for feedback,
--
Miika Komu miika@iki.fi http://www.iki.fi/miika/
^ permalink raw reply
* PPP dropping packets?
From: Antti Salmela @ 2005-07-26 11:26 UTC (permalink / raw)
To: paulus; +Cc: netdev, linux-ppp
I'm running 2.6.13-rc3 now but have been suffering from weird stalls with my
PPPoE connection ever since my ISP made it a requirement, about two months
ago. I found out by listening outgoing ethernet interface with tcpdump that
when a stall occurs, it's not sending anything. Listening to ppp interface
shows packets being sent. Discussed this with PPPoE maintainer Michal
Ostrowski and tried some debugging code he sent me, but it didn't reveal
really anything.
Here are two tcpdump logs taken when a stall occured and while a ping to ppp peer
was running. My ip is 213.139.163.144, peer is 10.10.9.1.
tcpdump -i ppp0 -n:
11:13:55.710515 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 1, length 64
11:13:56.709827 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 2, length 64
11:13:57.709842 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 3, length 64
11:13:58.709848 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 4, length 64
11:13:58.874515 IP 213.139.169.82.6668 > 213.139.163.144.58747: . 1689088981:1689089019(38) ack 1926745953 win 8499 <nop,nop,timestamp 66136067 4460534>
11:13:58.874610 IP 213.139.163.144.58747 > 213.139.169.82.6668: . ack 38 win 12424 <nop,nop,timestamp 4465549 66136067,nop,nop,sack 1 {0:38}>
11:13:59.709864 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 5, length 64
11:14:00.709869 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 6, length 64
11:14:01.709884 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 7, length 64
11:14:02.588471 IP 213.139.163.144.48587 > 207.46.0.35.1863: P 3889301623:3889301628(5) ack 519932196 win 2264 <nop,nop,timestamp 3228061 20203750>
11:14:02.709889 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 8, length 64
11:14:03.024512 IP 213.139.163.144.57511 > 207.46.0.63.1863: P 3897420679:3897420684(5) ack 1231695246 win 2264 <nop,nop,timestamp 3228170 20038047>
11:14:03.709905 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 9, length 64
11:14:04.505177 IP 216.239.59.99.80 > 213.139.163.144.44690: F 2225588483:2225588483(0) ack 4125655007 win 8190
11:14:04.544579 IP 213.139.163.144.44690 > 216.239.59.99.80: . ack 1 win 14300
11:14:04.709928 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 10, length 64
11:14:05.512052 IP 216.239.59.99.80 > 213.139.163.144.44690: F 0:0(0) ack 1 win 8190
11:14:05.512256 IP 213.139.163.144.44690 > 216.239.59.99.80: . ack 1 win 14300
11:14:05.709934 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 11, length 64
11:14:06.372979 IP 213.139.163.144.44690 > 216.239.59.99.80: F 1:1(0) ack 1 win 14300
11:14:06.628684 IP 213.139.163.144.44690 > 216.239.59.99.80: F 1:1(0) ack 1 win 14300
11:14:06.709947 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 12, length 64
11:14:07.140722 IP 213.139.163.144.44690 > 216.239.59.99.80: F 1:1(0) ack 1 win 14300
11:14:07.522432 IP 216.239.59.99.80 > 213.139.163.144.44690: F 0:0(0) ack 1 win 8190
11:14:07.522663 IP 213.139.163.144.44690 > 216.239.59.99.80: . ack 1 win 14300
11:14:07.710004 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 13, length 64
11:14:08.164751 IP 213.139.163.144.44690 > 216.239.59.99.80: F 1:1(0) ack 1 win 14300
11:14:08.709979 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 14, length 64
11:14:08.879946 IP 213.139.163.144.58747 > 213.139.169.82.6668: P 1:19(18) ack 38 win 12424 <nop,nop,timestamp 4466550 66136067>
11:14:09.710183 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 15, length 64
11:14:10.212868 IP 213.139.163.144.44690 > 216.239.59.99.80: F 1:1(0) ack 1 win 14300
11:14:10.710024 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 16, length 64
11:14:11.532770 IP 216.239.59.99.80 > 213.139.163.144.44690: F 0:0(0) ack 1 win 8190
11:14:11.532938 IP 213.139.163.144.44690 > 216.239.59.99.80: . ack 1 win 14300
11:14:11.710029 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 17, length 64
11:14:12.710065 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 18, length 64
11:14:12.710896 IP 10.10.9.1 > 213.139.163.144: ICMP echo reply seq 18, length 64
11:14:13.720235 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 19, length 64
11:14:13.721049 IP 10.10.9.1 > 213.139.163.144: ICMP echo reply seq 19, length 64
11:14:14.309102 IP 213.139.163.144.44690 > 216.239.59.99.80: F 1:1(0) ack 1 win 14300
11:14:14.363621 IP 216.239.59.99.80 > 213.139.163.144.44690: . ack 2 win 8190
11:14:14.730079 IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 20, length 64
11:14:14.730918 IP 10.10.9.1 > 213.139.163.144: ICMP echo reply seq 20, length 64
tcpdump -i eth0 -n:
11:13:54.765999 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:13:56.145144 PPPoE [ses 0x73ec] IP 216.221.122.20.40199 > 82.128.195.209.445: S 1710730503:1710730503(0) win 64240 <mss 1380,nop,nop,sackOK>
11:13:56.769516 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:13:57.164130 PPPoE [ses 0x6c4a] LCP, Echo-Request (0x09), id 100, length 14
11:13:58.767785 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:13:58.874515 PPPoE [ses 0x74c7] IP 213.139.169.82.6668 > 213.139.163.144.58747: . 1689088981:1689089019(38) ack 1926745953 win 8499 <nop,nop,timestamp 66136067 4460534>
11:14:00.772107 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:14:02.782147 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:14:04.505177 PPPoE [ses 0x74c7] IP 216.239.59.99.80 > 213.139.163.144.44690: F 2225588483:2225588483(0) ack 4125655007 win 8190
11:14:04.767610 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:14:05.512052 PPPoE [ses 0x74c7] IP 216.239.59.99.80 > 213.139.163.144.44690: F 0:0(0) ack 1 win 8190
11:14:06.769225 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:14:07.522432 PPPoE [ses 0x74c7] IP 216.239.59.99.80 > 213.139.163.144.44690: F 0:0(0) ack 1 win 8190
11:14:08.766445 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:14:10.710040 PPPoE [ses 0x74c7] IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 16, length 64
11:14:10.766339 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:14:11.532770 PPPoE [ses 0x74c7] IP 216.239.59.99.80 > 213.139.163.144.44690: F 0:0(0) ack 1 win 8190
11:14:11.532950 PPPoE [ses 0x74c7] IP 213.139.163.144.44690 > 216.239.59.99.80: . ack 1 win 14300
11:14:11.710042 PPPoE [ses 0x74c7] IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 17, length 64
11:14:12.710085 PPPoE [ses 0x74c7] IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 18, length 64
11:14:12.710896 PPPoE [ses 0x74c7] IP 10.10.9.1 > 213.139.163.144: ICMP echo reply seq 18, length 64
11:14:12.767266 802.1d config 8000.00:06:28:3a:8c:83.8020 root 61cf.00:05:5f:50:60:00 pathcost 27 age 3 max 20 hello 2 fdelay 15
11:14:13.720253 PPPoE [ses 0x74c7] IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 19, length 64
11:14:13.721049 PPPoE [ses 0x74c7] IP 10.10.9.1 > 213.139.163.144: ICMP echo reply seq 19, length 64
11:14:14.309119 PPPoE [ses 0x74c7] IP 213.139.163.144.44690 > 216.239.59.99.80: F 1:1(0) ack 1 win 14300
11:14:14.316805 PPPoE [ses 0x6738] LCP, Echo-Request (0x09), id 73, length 14
11:14:14.363621 PPPoE [ses 0x74c7] IP 216.239.59.99.80 > 213.139.163.144.44690: . ack 2 win 8190
11:14:14.730096 PPPoE [ses 0x74c7] IP 213.139.163.144 > 10.10.9.1: ICMP echo request seq 20, length 64
11:14:14.730918 PPPoE [ses 0x74c7] IP 10.10.9.1 > 213.139.163.144: ICMP echo reply seq 20, length 64
--- linux-2.6.12-rc6/drivers/net/pppoe.c 2005-06-09 17:35:59.000000000 +0300
+++ linux-2.6.13-rc3/drivers/net/pppoe.c 2005-07-15 14:58:30.000000000 +0300
@@ -849,7 +849,9 @@
int headroom = skb_headroom(skb);
int data_len = skb->len;
struct sk_buff *skb2;
+ int line = 0;
+ line = __LINE__;
if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
goto abort;
@@ -859,6 +861,7 @@
hdr.sid = po->num;
hdr.length = htons(skb->len);
+ line = __LINE__;
if (!dev)
goto abort;
@@ -868,6 +871,7 @@
sizeof(struct pppoe_hdr) +
dev->hard_header_len);
+ line = __LINE__;
if (skb2 == NULL)
goto abort;
@@ -897,6 +901,7 @@
* but free it in case of success.
*/
+ line = __LINE__;
if (dev_queue_xmit(skb2) < 0)
goto abort;
@@ -904,6 +909,7 @@
return 1;
abort:
+ printk("pppoe xmit abort: %d\n", line);
return 0;
}
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.13-rc3
# Thu Jul 14 22:10:48 2005
#
CONFIG_X86=y
CONFIG_MMU=y
CONFIG_UID16=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y
#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
CONFIG_CLEAN_COMPILE=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
#
# General setup
#
CONFIG_LOCALVERSION=""
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
# CONFIG_AUDIT is not set
CONFIG_HOTPLUG=y
CONFIG_KOBJECT_UEVENT=y
# CONFIG_IKCONFIG is not set
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_SHMEM=y
CONFIG_CC_ALIGN_FUNCTIONS=0
CONFIG_CC_ALIGN_LABELS=0
CONFIG_CC_ALIGN_LOOPS=0
CONFIG_CC_ALIGN_JUMPS=0
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
#
# Loadable module support
#
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
# CONFIG_MODULE_FORCE_UNLOAD is not set
CONFIG_OBSOLETE_MODPARM=y
CONFIG_MODVERSIONS=y
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_KMOD=y
#
# Processor type and features
#
CONFIG_X86_PC=y
# CONFIG_X86_ELAN is not set
# CONFIG_X86_VOYAGER is not set
# CONFIG_X86_NUMAQ is not set
# CONFIG_X86_SUMMIT is not set
# CONFIG_X86_BIGSMP is not set
# CONFIG_X86_VISWS is not set
# CONFIG_X86_GENERICARCH is not set
# CONFIG_X86_ES7000 is not set
# CONFIG_M386 is not set
# CONFIG_M486 is not set
# CONFIG_M586 is not set
# CONFIG_M586TSC is not set
# CONFIG_M586MMX is not set
# CONFIG_M686 is not set
# CONFIG_MPENTIUMII is not set
# CONFIG_MPENTIUMIII is not set
# CONFIG_MPENTIUMM is not set
# CONFIG_MPENTIUM4 is not set
# CONFIG_MK6 is not set
CONFIG_MK7=y
# CONFIG_MK8 is not set
# CONFIG_MCRUSOE is not set
# CONFIG_MEFFICEON is not set
# CONFIG_MWINCHIPC6 is not set
# CONFIG_MWINCHIP2 is not set
# CONFIG_MWINCHIP3D is not set
# CONFIG_MGEODEGX1 is not set
# CONFIG_MCYRIXIII is not set
# CONFIG_MVIAC3_2 is not set
# CONFIG_X86_GENERIC is not set
CONFIG_X86_CMPXCHG=y
CONFIG_X86_XADD=y
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_X86_WP_WORKS_OK=y
CONFIG_X86_INVLPG=y
CONFIG_X86_BSWAP=y
CONFIG_X86_POPAD_OK=y
CONFIG_X86_GOOD_APIC=y
CONFIG_X86_INTEL_USERCOPY=y
CONFIG_X86_USE_PPRO_CHECKSUM=y
CONFIG_X86_USE_3DNOW=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
# CONFIG_SMP is not set
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
CONFIG_X86_UP_APIC=y
# CONFIG_X86_UP_IOAPIC is not set
CONFIG_X86_LOCAL_APIC=y
CONFIG_X86_TSC=y
CONFIG_X86_MCE=y
CONFIG_X86_MCE_NONFATAL=y
# CONFIG_X86_MCE_P4THERMAL is not set
# CONFIG_TOSHIBA is not set
# CONFIG_I8K is not set
# CONFIG_X86_REBOOTFIXUPS is not set
# CONFIG_MICROCODE is not set
# CONFIG_X86_MSR is not set
# CONFIG_X86_CPUID is not set
#
# Firmware Drivers
#
CONFIG_EDD=y
# CONFIG_NOHIGHMEM is not set
CONFIG_HIGHMEM4G=y
# CONFIG_HIGHMEM64G is not set
CONFIG_HIGHMEM=y
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_HIGHPTE is not set
# CONFIG_MATH_EMULATION is not set
CONFIG_MTRR=y
# CONFIG_EFI is not set
CONFIG_REGPARM=y
CONFIG_SECCOMP=y
CONFIG_HZ_100=y
# CONFIG_HZ_250 is not set
# CONFIG_HZ_1000 is not set
CONFIG_HZ=100
CONFIG_PHYSICAL_START=0x100000
# CONFIG_KEXEC is not set
#
# Power management options (ACPI, APM)
#
CONFIG_PM=y
# CONFIG_PM_DEBUG is not set
# CONFIG_SOFTWARE_SUSPEND is not set
#
# ACPI (Advanced Configuration and Power Interface) Support
#
CONFIG_ACPI=y
CONFIG_ACPI_BOOT=y
CONFIG_ACPI_INTERPRETER=y
# CONFIG_ACPI_SLEEP is not set
# CONFIG_ACPI_AC is not set
# CONFIG_ACPI_BATTERY is not set
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_VIDEO=m
CONFIG_ACPI_HOTKEY=m
CONFIG_ACPI_FAN=y
CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_THERMAL=y
# CONFIG_ACPI_ASUS is not set
CONFIG_ACPI_IBM=m
# CONFIG_ACPI_TOSHIBA is not set
CONFIG_ACPI_BLACKLIST_YEAR=0
# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_BUS=y
CONFIG_ACPI_EC=y
CONFIG_ACPI_POWER=y
CONFIG_ACPI_PCI=y
CONFIG_ACPI_SYSTEM=y
CONFIG_X86_PM_TIMER=y
# CONFIG_ACPI_CONTAINER is not set
#
# APM (Advanced Power Management) BIOS Support
#
# CONFIG_APM is not set
#
# CPU Frequency scaling
#
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_TABLE=y
# CONFIG_CPU_FREQ_DEBUG is not set
CONFIG_CPU_FREQ_STAT=y
# CONFIG_CPU_FREQ_STAT_DETAILS is not set
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
#
# CPUFreq processor drivers
#
CONFIG_X86_ACPI_CPUFREQ=y
# CONFIG_X86_POWERNOW_K6 is not set
CONFIG_X86_POWERNOW_K7=y
CONFIG_X86_POWERNOW_K7_ACPI=y
# CONFIG_X86_POWERNOW_K8 is not set
# CONFIG_X86_GX_SUSPMOD is not set
# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
# CONFIG_X86_SPEEDSTEP_ICH is not set
# CONFIG_X86_SPEEDSTEP_SMI is not set
# CONFIG_X86_P4_CLOCKMOD is not set
# CONFIG_X86_CPUFREQ_NFORCE2 is not set
# CONFIG_X86_LONGRUN is not set
# CONFIG_X86_LONGHAUL is not set
#
# shared options
#
# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
# CONFIG_X86_SPEEDSTEP_LIB is not set
#
# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
#
CONFIG_PCI=y
# CONFIG_PCI_GOBIOS is not set
# CONFIG_PCI_GOMMCONFIG is not set
# CONFIG_PCI_GODIRECT is not set
CONFIG_PCI_GOANY=y
CONFIG_PCI_BIOS=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
# CONFIG_PCIEPORTBUS is not set
# CONFIG_PCI_LEGACY_PROC is not set
CONFIG_PCI_NAMES=y
# CONFIG_PCI_DEBUG is not set
CONFIG_ISA_DMA_API=y
CONFIG_ISA=y
# CONFIG_EISA is not set
# CONFIG_MCA is not set
# CONFIG_SCx200 is not set
#
# PCCARD (PCMCIA/CardBus) support
#
# CONFIG_PCCARD is not set
#
# PCI Hotplug Support
#
# CONFIG_HOTPLUG_PCI is not set
#
# Executable file formats
#
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_AOUT is not set
CONFIG_BINFMT_MISC=y
#
# Networking
#
CONFIG_NET=y
#
# Networking options
#
CONFIG_PACKET=y
CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
CONFIG_XFRM=y
CONFIG_XFRM_USER=y
CONFIG_NET_KEY=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_FIB_HASH=y
# CONFIG_IP_FIB_TRIE is not set
CONFIG_IP_MULTIPLE_TABLES=y
# CONFIG_IP_ROUTE_FWMARK is not set
CONFIG_IP_ROUTE_MULTIPATH=y
# CONFIG_IP_ROUTE_MULTIPATH_CACHED is not set
# CONFIG_IP_ROUTE_VERBOSE is not set
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
# CONFIG_IP_PNP_RARP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
CONFIG_IP_MROUTE=y
# CONFIG_IP_PIMSM_V1 is not set
CONFIG_IP_PIMSM_V2=y
# CONFIG_ARPD is not set
CONFIG_SYN_COOKIES=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
CONFIG_INET_TUNNEL=y
CONFIG_IP_TCPDIAG=y
CONFIG_IP_TCPDIAG_IPV6=y
CONFIG_TCP_CONG_ADVANCED=y
#
# TCP congestion control
#
CONFIG_TCP_CONG_BIC=y
CONFIG_TCP_CONG_WESTWOOD=y
CONFIG_TCP_CONG_HTCP=y
# CONFIG_TCP_CONG_HSTCP is not set
# CONFIG_TCP_CONG_HYBLA is not set
CONFIG_TCP_CONG_VEGAS=y
# CONFIG_TCP_CONG_SCALABLE is not set
#
# IP: Virtual Server Configuration
#
# CONFIG_IP_VS is not set
CONFIG_IPV6=y
# CONFIG_IPV6_PRIVACY is not set
CONFIG_INET6_AH=m
CONFIG_INET6_ESP=m
CONFIG_INET6_IPCOMP=m
CONFIG_INET6_TUNNEL=m
# CONFIG_IPV6_TUNNEL is not set
CONFIG_NETFILTER=y
# CONFIG_NETFILTER_DEBUG is not set
#
# IP: Netfilter Configuration
#
CONFIG_IP_NF_CONNTRACK=y
# CONFIG_IP_NF_CT_ACCT is not set
# CONFIG_IP_NF_CONNTRACK_MARK is not set
# CONFIG_IP_NF_CT_PROTO_SCTP is not set
CONFIG_IP_NF_FTP=y
CONFIG_IP_NF_IRC=y
# CONFIG_IP_NF_TFTP is not set
# CONFIG_IP_NF_AMANDA is not set
# CONFIG_IP_NF_QUEUE is not set
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_MATCH_LIMIT=y
# CONFIG_IP_NF_MATCH_IPRANGE is not set
CONFIG_IP_NF_MATCH_MAC=y
CONFIG_IP_NF_MATCH_PKTTYPE=y
CONFIG_IP_NF_MATCH_MARK=y
CONFIG_IP_NF_MATCH_MULTIPORT=y
CONFIG_IP_NF_MATCH_TOS=y
# CONFIG_IP_NF_MATCH_RECENT is not set
CONFIG_IP_NF_MATCH_ECN=y
CONFIG_IP_NF_MATCH_DSCP=y
CONFIG_IP_NF_MATCH_AH_ESP=y
CONFIG_IP_NF_MATCH_LENGTH=y
CONFIG_IP_NF_MATCH_TTL=y
CONFIG_IP_NF_MATCH_TCPMSS=y
CONFIG_IP_NF_MATCH_HELPER=y
CONFIG_IP_NF_MATCH_STATE=y
CONFIG_IP_NF_MATCH_CONNTRACK=y
CONFIG_IP_NF_MATCH_OWNER=y
# CONFIG_IP_NF_MATCH_ADDRTYPE is not set
# CONFIG_IP_NF_MATCH_REALM is not set
# CONFIG_IP_NF_MATCH_SCTP is not set
# CONFIG_IP_NF_MATCH_COMMENT is not set
# CONFIG_IP_NF_MATCH_HASHLIMIT is not set
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
CONFIG_IP_NF_TARGET_LOG=y
CONFIG_IP_NF_TARGET_ULOG=y
CONFIG_IP_NF_TARGET_TCPMSS=y
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_NAT_NEEDED=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
CONFIG_IP_NF_TARGET_REDIRECT=y
# CONFIG_IP_NF_TARGET_NETMAP is not set
# CONFIG_IP_NF_TARGET_SAME is not set
# CONFIG_IP_NF_NAT_SNMP_BASIC is not set
CONFIG_IP_NF_NAT_IRC=y
CONFIG_IP_NF_NAT_FTP=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_TARGET_TOS=y
CONFIG_IP_NF_TARGET_ECN=y
CONFIG_IP_NF_TARGET_DSCP=y
CONFIG_IP_NF_TARGET_MARK=y
# CONFIG_IP_NF_TARGET_CLASSIFY is not set
# CONFIG_IP_NF_RAW is not set
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
# CONFIG_IP_NF_ARP_MANGLE is not set
#
# IPv6: Netfilter Configuration (EXPERIMENTAL)
#
CONFIG_IP6_NF_QUEUE=m
CONFIG_IP6_NF_IPTABLES=m
CONFIG_IP6_NF_MATCH_LIMIT=m
CONFIG_IP6_NF_MATCH_MAC=m
# CONFIG_IP6_NF_MATCH_RT is not set
# CONFIG_IP6_NF_MATCH_OPTS is not set
# CONFIG_IP6_NF_MATCH_FRAG is not set
# CONFIG_IP6_NF_MATCH_HL is not set
CONFIG_IP6_NF_MATCH_MULTIPORT=m
CONFIG_IP6_NF_MATCH_OWNER=m
CONFIG_IP6_NF_MATCH_MARK=m
# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
# CONFIG_IP6_NF_MATCH_AHESP is not set
CONFIG_IP6_NF_MATCH_LENGTH=m
CONFIG_IP6_NF_MATCH_EUI64=m
CONFIG_IP6_NF_FILTER=m
CONFIG_IP6_NF_TARGET_LOG=m
CONFIG_IP6_NF_MANGLE=m
CONFIG_IP6_NF_TARGET_MARK=m
# CONFIG_IP6_NF_RAW is not set
#
# SCTP Configuration (EXPERIMENTAL)
#
CONFIG_IP_SCTP=m
# CONFIG_SCTP_DBG_MSG is not set
# CONFIG_SCTP_DBG_OBJCNT is not set
# CONFIG_SCTP_HMAC_NONE is not set
CONFIG_SCTP_HMAC_SHA1=y
# CONFIG_SCTP_HMAC_MD5 is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
CONFIG_VLAN_8021Q=y
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
CONFIG_NET_SCHED=y
# CONFIG_NET_SCH_CLK_JIFFIES is not set
# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set
CONFIG_NET_SCH_CLK_CPU=y
CONFIG_NET_SCH_CBQ=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_HFSC=y
CONFIG_NET_SCH_PRIO=y
CONFIG_NET_SCH_RED=y
CONFIG_NET_SCH_SFQ=y
CONFIG_NET_SCH_TEQL=y
CONFIG_NET_SCH_TBF=y
CONFIG_NET_SCH_GRED=y
CONFIG_NET_SCH_DSMARK=y
# CONFIG_NET_SCH_NETEM is not set
CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_QOS=y
CONFIG_NET_ESTIMATOR=y
CONFIG_NET_CLS=y
# CONFIG_NET_CLS_BASIC is not set
CONFIG_NET_CLS_TCINDEX=y
CONFIG_NET_CLS_ROUTE4=y
CONFIG_NET_CLS_ROUTE=y
CONFIG_NET_CLS_FW=y
CONFIG_NET_CLS_U32=y
# CONFIG_CLS_U32_PERF is not set
# CONFIG_NET_CLS_IND is not set
# CONFIG_CLS_U32_MARK is not set
CONFIG_NET_CLS_RSVP=y
CONFIG_NET_CLS_RSVP6=y
# CONFIG_NET_EMATCH is not set
# CONFIG_NET_CLS_ACT is not set
CONFIG_NET_CLS_POLICE=y
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
# CONFIG_HAMRADIO is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
#
# Device Drivers
#
#
# Generic Driver Options
#
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=m
# CONFIG_DEBUG_DRIVER is not set
#
# Memory Technology Devices (MTD)
#
# CONFIG_MTD is not set
#
# Parallel port support
#
# CONFIG_PARPORT is not set
#
# Plug and Play support
#
CONFIG_PNP=y
# CONFIG_PNP_DEBUG is not set
#
# Protocols
#
CONFIG_ISAPNP=y
# CONFIG_PNPBIOS is not set
CONFIG_PNPACPI=y
#
# Block devices
#
CONFIG_BLK_DEV_FD=y
# CONFIG_BLK_DEV_XD is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
# CONFIG_BLK_DEV_UMEM is not set
# CONFIG_BLK_DEV_COW_COMMON is not set
CONFIG_BLK_DEV_LOOP=m
CONFIG_BLK_DEV_CRYPTOLOOP=m
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_SX8 is not set
# CONFIG_BLK_DEV_RAM is not set
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_INITRAMFS_SOURCE=""
# CONFIG_LBD is not set
# CONFIG_CDROM_PKTCDVD is not set
#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=y
# CONFIG_ATA_OVER_ETH is not set
#
# ATA/ATAPI/MFM/RLL support
#
CONFIG_IDE=y
CONFIG_BLK_DEV_IDE=y
#
# Please see Documentation/ide.txt for help/info on IDE drives
#
# CONFIG_BLK_DEV_IDE_SATA is not set
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
CONFIG_IDEDISK_MULTI_MODE=y
CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDEFLOPPY is not set
# CONFIG_BLK_DEV_IDESCSI is not set
# CONFIG_IDE_TASK_IOCTL is not set
#
# IDE chipset support/bugfixes
#
CONFIG_IDE_GENERIC=m
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_IDEPNP is not set
CONFIG_BLK_DEV_IDEPCI=y
CONFIG_IDEPCI_SHARE_IRQ=y
# CONFIG_BLK_DEV_OFFBOARD is not set
CONFIG_BLK_DEV_GENERIC=y
# CONFIG_BLK_DEV_OPTI621 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
CONFIG_BLK_DEV_IDEDMA_PCI=y
# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
CONFIG_IDEDMA_PCI_AUTO=y
# CONFIG_IDEDMA_ONLYDISK is not set
# CONFIG_BLK_DEV_AEC62XX is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD74XX is not set
# CONFIG_BLK_DEV_ATIIXP is not set
# CONFIG_BLK_DEV_CMD64X is not set
# CONFIG_BLK_DEV_TRIFLEX is not set
# CONFIG_BLK_DEV_CY82C693 is not set
# CONFIG_BLK_DEV_CS5520 is not set
# CONFIG_BLK_DEV_CS5530 is not set
# CONFIG_BLK_DEV_HPT34X is not set
# CONFIG_BLK_DEV_HPT366 is not set
# CONFIG_BLK_DEV_SC1200 is not set
# CONFIG_BLK_DEV_PIIX is not set
# CONFIG_BLK_DEV_IT821X is not set
# CONFIG_BLK_DEV_NS87415 is not set
CONFIG_BLK_DEV_PDC202XX_OLD=y
# CONFIG_PDC202XX_BURST is not set
CONFIG_BLK_DEV_PDC202XX_NEW=y
# CONFIG_PDC202XX_FORCE is not set
# CONFIG_BLK_DEV_SVWKS is not set
# CONFIG_BLK_DEV_SIIMAGE is not set
# CONFIG_BLK_DEV_SIS5513 is not set
# CONFIG_BLK_DEV_SLC90E66 is not set
# CONFIG_BLK_DEV_TRM290 is not set
CONFIG_BLK_DEV_VIA82CXXX=y
# CONFIG_IDE_ARM is not set
# CONFIG_IDE_CHIPSETS is not set
CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDEDMA_IVB is not set
CONFIG_IDEDMA_AUTO=y
# CONFIG_BLK_DEV_HD is not set
#
# SCSI device support
#
CONFIG_SCSI=m
CONFIG_SCSI_PROC_FS=y
#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=m
# CONFIG_CHR_DEV_ST is not set
# CONFIG_CHR_DEV_OSST is not set
CONFIG_BLK_DEV_SR=m
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_CHR_DEV_SG=m
# CONFIG_CHR_DEV_SCH is not set
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
# CONFIG_SCSI_MULTI_LUN is not set
# CONFIG_SCSI_CONSTANTS is not set
# CONFIG_SCSI_LOGGING is not set
#
# SCSI Transport Attributes
#
# CONFIG_SCSI_SPI_ATTRS is not set
# CONFIG_SCSI_FC_ATTRS is not set
# CONFIG_SCSI_ISCSI_ATTRS is not set
#
# SCSI low-level drivers
#
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_3W_9XXX is not set
# CONFIG_SCSI_7000FASST is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AHA152X is not set
# CONFIG_SCSI_AHA1542 is not set
# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_AIC79XX is not set
# CONFIG_SCSI_DPT_I2O is not set
# CONFIG_SCSI_IN2000 is not set
# CONFIG_MEGARAID_NEWGEN is not set
# CONFIG_MEGARAID_LEGACY is not set
# CONFIG_SCSI_SATA is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_DTC3280 is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
# CONFIG_SCSI_IPS is not set
# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_SYM53C8XX_2 is not set
# CONFIG_SCSI_IPR is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_PSI240I is not set
# CONFIG_SCSI_QLOGIC_FAS is not set
# CONFIG_SCSI_QLOGIC_FC is not set
# CONFIG_SCSI_QLOGIC_1280 is not set
CONFIG_SCSI_QLA2XXX=m
# CONFIG_SCSI_QLA21XX is not set
# CONFIG_SCSI_QLA22XX is not set
# CONFIG_SCSI_QLA2300 is not set
# CONFIG_SCSI_QLA2322 is not set
# CONFIG_SCSI_QLA6312 is not set
# CONFIG_SCSI_LPFC is not set
# CONFIG_SCSI_SYM53C416 is not set
# CONFIG_SCSI_DC395x is not set
# CONFIG_SCSI_DC390T is not set
# CONFIG_SCSI_T128 is not set
# CONFIG_SCSI_U14_34F is not set
# CONFIG_SCSI_ULTRASTOR is not set
# CONFIG_SCSI_NSP32 is not set
# CONFIG_SCSI_DEBUG is not set
#
# Old CD-ROM drivers (not SCSI, not IDE)
#
# CONFIG_CD_NO_IDESCSI is not set
#
# Multi-device support (RAID and LVM)
#
CONFIG_MD=y
CONFIG_BLK_DEV_MD=y
# CONFIG_MD_LINEAR is not set
# CONFIG_MD_RAID0 is not set
CONFIG_MD_RAID1=y
# CONFIG_MD_RAID10 is not set
# CONFIG_MD_RAID5 is not set
# CONFIG_MD_RAID6 is not set
# CONFIG_MD_MULTIPATH is not set
# CONFIG_MD_FAULTY is not set
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
CONFIG_DM_SNAPSHOT=y
CONFIG_DM_MIRROR=y
# CONFIG_DM_ZERO is not set
# CONFIG_DM_MULTIPATH is not set
#
# Fusion MPT device support
#
# CONFIG_FUSION is not set
# CONFIG_FUSION_SPI is not set
# CONFIG_FUSION_FC is not set
#
# IEEE 1394 (FireWire) support
#
# CONFIG_IEEE1394 is not set
#
# I2O device support
#
# CONFIG_I2O is not set
#
# Network device support
#
CONFIG_NETDEVICES=y
CONFIG_DUMMY=m
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_NET_SB1000 is not set
#
# ARCnet devices
#
# CONFIG_ARCNET is not set
#
# Ethernet (10 or 100Mbit)
#
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
# CONFIG_HAPPYMEAL is not set
# CONFIG_SUNGEM is not set
CONFIG_NET_VENDOR_3COM=y
# CONFIG_EL1 is not set
# CONFIG_EL2 is not set
# CONFIG_ELPLUS is not set
# CONFIG_EL16 is not set
# CONFIG_EL3 is not set
# CONFIG_3C515 is not set
CONFIG_VORTEX=y
# CONFIG_TYPHOON is not set
# CONFIG_LANCE is not set
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_VENDOR_RACAL is not set
#
# Tulip family network device support
#
# CONFIG_NET_TULIP is not set
# CONFIG_AT1700 is not set
# CONFIG_DEPCA is not set
# CONFIG_HP100 is not set
# CONFIG_NET_ISA is not set
# CONFIG_NET_PCI is not set
# CONFIG_NET_POCKET is not set
#
# Ethernet (1000 Mbit)
#
# CONFIG_ACENIC is not set
# CONFIG_DL2K is not set
# CONFIG_E1000 is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_R8169 is not set
# CONFIG_SKGE is not set
# CONFIG_SK98LIN is not set
# CONFIG_TIGON3 is not set
# CONFIG_BNX2 is not set
#
# Ethernet (10000 Mbit)
#
# CONFIG_IXGB is not set
# CONFIG_S2IO is not set
#
# Token Ring devices
#
# CONFIG_TR is not set
#
# Wireless LAN (non-hamradio)
#
# CONFIG_NET_RADIO is not set
#
# Wan interfaces
#
# CONFIG_WAN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
CONFIG_PPP=y
# CONFIG_PPP_MULTILINK is not set
# CONFIG_PPP_FILTER is not set
# CONFIG_PPP_ASYNC is not set
# CONFIG_PPP_SYNC_TTY is not set
# CONFIG_PPP_DEFLATE is not set
# CONFIG_PPP_BSDCOMP is not set
CONFIG_PPPOE=y
# CONFIG_SLIP is not set
# CONFIG_NET_FC is not set
# CONFIG_SHAPER is not set
# CONFIG_NETCONSOLE is not set
#
# ISDN subsystem
#
# CONFIG_ISDN is not set
#
# Telephony Support
#
# CONFIG_PHONE is not set
#
# Input device support
#
CONFIG_INPUT=y
#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_TSDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set
#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_I8042=y
CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_CT82C710 is not set
# CONFIG_SERIO_PCIPS2 is not set
CONFIG_SERIO_LIBPS2=y
# CONFIG_SERIO_RAW is not set
# CONFIG_GAMEPORT is not set
#
# Character devices
#
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_SERIAL_NONSTANDARD is not set
#
# Serial drivers
#
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_CONSOLE is not set
# CONFIG_SERIAL_8250_ACPI is not set
CONFIG_SERIAL_8250_NR_UARTS=4
# CONFIG_SERIAL_8250_EXTENDED is not set
#
# Non-8250 serial port support
#
CONFIG_SERIAL_CORE=y
# CONFIG_SERIAL_JSM is not set
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=16
#
# IPMI
#
# CONFIG_IPMI_HANDLER is not set
#
# Watchdog Cards
#
CONFIG_WATCHDOG=y
# CONFIG_WATCHDOG_NOWAYOUT is not set
#
# Watchdog Device Drivers
#
CONFIG_SOFT_WATCHDOG=y
# CONFIG_ACQUIRE_WDT is not set
# CONFIG_ADVANTECH_WDT is not set
# CONFIG_ALIM1535_WDT is not set
# CONFIG_ALIM7101_WDT is not set
# CONFIG_SC520_WDT is not set
# CONFIG_EUROTECH_WDT is not set
# CONFIG_IB700_WDT is not set
# CONFIG_WAFER_WDT is not set
# CONFIG_I8XX_TCO is not set
# CONFIG_SC1200_WDT is not set
# CONFIG_60XX_WDT is not set
# CONFIG_CPU5_WDT is not set
# CONFIG_W83627HF_WDT is not set
# CONFIG_W83877F_WDT is not set
# CONFIG_MACHZ_WDT is not set
#
# ISA-based Watchdog Cards
#
# CONFIG_PCWATCHDOG is not set
# CONFIG_MIXCOMWD is not set
# CONFIG_WDT is not set
#
# PCI-based Watchdog Cards
#
# CONFIG_PCIPCWATCHDOG is not set
# CONFIG_WDTPCI is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_NVRAM is not set
CONFIG_RTC=y
# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
# CONFIG_SONYPI is not set
#
# Ftape, the floppy tape device driver
#
# CONFIG_FTAPE is not set
CONFIG_AGP=y
# CONFIG_AGP_ALI is not set
# CONFIG_AGP_ATI is not set
# CONFIG_AGP_AMD is not set
# CONFIG_AGP_AMD64 is not set
# CONFIG_AGP_INTEL is not set
# CONFIG_AGP_NVIDIA is not set
# CONFIG_AGP_SIS is not set
# CONFIG_AGP_SWORKS is not set
CONFIG_AGP_VIA=y
# CONFIG_AGP_EFFICEON is not set
# CONFIG_DRM is not set
# CONFIG_MWAVE is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_HPET is not set
# CONFIG_HANGCHECK_TIMER is not set
#
# TPM devices
#
# CONFIG_TCG_TPM is not set
#
# I2C support
#
CONFIG_I2C=m
CONFIG_I2C_CHARDEV=m
#
# I2C Algorithms
#
CONFIG_I2C_ALGOBIT=m
CONFIG_I2C_ALGOPCF=m
# CONFIG_I2C_ALGOPCA is not set
#
# I2C Hardware Bus support
#
CONFIG_I2C_ALI1535=m
CONFIG_I2C_ALI1563=m
CONFIG_I2C_ALI15X3=m
CONFIG_I2C_AMD756=m
# CONFIG_I2C_AMD756_S4882 is not set
CONFIG_I2C_AMD8111=m
CONFIG_I2C_ELEKTOR=m
CONFIG_I2C_I801=m
CONFIG_I2C_I810=m
CONFIG_I2C_PIIX4=m
CONFIG_I2C_ISA=m
CONFIG_I2C_NFORCE2=m
CONFIG_I2C_PARPORT_LIGHT=m
CONFIG_I2C_PROSAVAGE=m
CONFIG_I2C_SAVAGE4=m
CONFIG_SCx200_ACB=m
CONFIG_I2C_SIS5595=m
CONFIG_I2C_SIS630=m
CONFIG_I2C_SIS96X=m
# CONFIG_I2C_STUB is not set
CONFIG_I2C_VIA=m
CONFIG_I2C_VIAPRO=m
CONFIG_I2C_VOODOO3=m
# CONFIG_I2C_PCA_ISA is not set
CONFIG_I2C_SENSOR=m
#
# Miscellaneous I2C Chip support
#
CONFIG_SENSORS_DS1337=m
# CONFIG_SENSORS_DS1374 is not set
CONFIG_SENSORS_EEPROM=m
CONFIG_SENSORS_PCF8574=m
# CONFIG_SENSORS_PCA9539 is not set
CONFIG_SENSORS_PCF8591=m
CONFIG_SENSORS_RTC8564=m
# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
# CONFIG_I2C_DEBUG_CHIP is not set
#
# Dallas's 1-wire bus
#
# CONFIG_W1 is not set
#
# Hardware Monitoring support
#
CONFIG_HWMON=y
CONFIG_SENSORS_ADM1021=m
CONFIG_SENSORS_ADM1025=m
CONFIG_SENSORS_ADM1026=m
CONFIG_SENSORS_ADM1031=m
CONFIG_SENSORS_ADM9240=m
CONFIG_SENSORS_ASB100=m
CONFIG_SENSORS_ATXP1=m
CONFIG_SENSORS_DS1621=m
CONFIG_SENSORS_FSCHER=m
# CONFIG_SENSORS_FSCPOS is not set
CONFIG_SENSORS_GL518SM=m
CONFIG_SENSORS_GL520SM=m
CONFIG_SENSORS_IT87=m
CONFIG_SENSORS_LM63=m
CONFIG_SENSORS_LM75=m
CONFIG_SENSORS_LM77=m
CONFIG_SENSORS_LM78=m
CONFIG_SENSORS_LM80=m
CONFIG_SENSORS_LM83=m
CONFIG_SENSORS_LM85=m
CONFIG_SENSORS_LM87=m
CONFIG_SENSORS_LM90=m
CONFIG_SENSORS_LM92=m
CONFIG_SENSORS_MAX1619=m
CONFIG_SENSORS_PC87360=m
CONFIG_SENSORS_SIS5595=m
# CONFIG_SENSORS_SMSC47M1 is not set
# CONFIG_SENSORS_SMSC47B397 is not set
CONFIG_SENSORS_VIA686A=m
CONFIG_SENSORS_W83781D=m
CONFIG_SENSORS_W83L785TS=m
CONFIG_SENSORS_W83627HF=m
CONFIG_SENSORS_W83627EHF=m
# CONFIG_HWMON_DEBUG_CHIP is not set
#
# Misc devices
#
# CONFIG_IBM_ASM is not set
#
# Multimedia devices
#
CONFIG_VIDEO_DEV=m
#
# Video For Linux
#
#
# Video Adapters
#
# CONFIG_VIDEO_BT848 is not set
# CONFIG_VIDEO_PMS is not set
# CONFIG_VIDEO_CPIA is not set
# CONFIG_VIDEO_SAA5246A is not set
# CONFIG_VIDEO_SAA5249 is not set
# CONFIG_TUNER_3036 is not set
# CONFIG_VIDEO_STRADIS is not set
# CONFIG_VIDEO_ZORAN is not set
# CONFIG_VIDEO_SAA7134 is not set
# CONFIG_VIDEO_MXB is not set
# CONFIG_VIDEO_DPC is not set
# CONFIG_VIDEO_HEXIUM_ORION is not set
# CONFIG_VIDEO_HEXIUM_GEMINI is not set
# CONFIG_VIDEO_CX88 is not set
# CONFIG_VIDEO_OVCAMCHIP is not set
#
# Radio Adapters
#
# CONFIG_RADIO_CADET is not set
# CONFIG_RADIO_RTRACK is not set
# CONFIG_RADIO_RTRACK2 is not set
# CONFIG_RADIO_AZTECH is not set
# CONFIG_RADIO_GEMTEK is not set
# CONFIG_RADIO_GEMTEK_PCI is not set
# CONFIG_RADIO_MAXIRADIO is not set
# CONFIG_RADIO_MAESTRO is not set
# CONFIG_RADIO_SF16FMI is not set
# CONFIG_RADIO_SF16FMR2 is not set
# CONFIG_RADIO_TERRATEC is not set
# CONFIG_RADIO_TRUST is not set
# CONFIG_RADIO_TYPHOON is not set
# CONFIG_RADIO_ZOLTRIX is not set
#
# Digital Video Broadcasting Devices
#
CONFIG_DVB=y
CONFIG_DVB_CORE=m
#
# Supported SAA7146 based PCI Adapters
#
CONFIG_DVB_AV7110=m
CONFIG_DVB_AV7110_OSD=y
CONFIG_DVB_BUDGET=m
CONFIG_DVB_BUDGET_CI=m
# CONFIG_DVB_BUDGET_AV is not set
CONFIG_DVB_BUDGET_PATCH=m
#
# Supported FlexCopII (B2C2) Adapters
#
# CONFIG_DVB_B2C2_FLEXCOP is not set
#
# Supported BT878 Adapters
#
#
# Supported Pluto2 Adapters
#
# CONFIG_DVB_PLUTO2 is not set
#
# Supported DVB Frontends
#
#
# Customise DVB Frontends
#
#
# DVB-S (satellite) frontends
#
CONFIG_DVB_STV0299=m
CONFIG_DVB_CX24110=m
CONFIG_DVB_TDA8083=m
CONFIG_DVB_TDA80XX=m
CONFIG_DVB_MT312=m
CONFIG_DVB_VES1X93=m
CONFIG_DVB_S5H1420=m
#
# DVB-T (terrestrial) frontends
#
CONFIG_DVB_SP8870=m
CONFIG_DVB_SP887X=m
CONFIG_DVB_CX22700=m
CONFIG_DVB_CX22702=m
CONFIG_DVB_L64781=m
CONFIG_DVB_TDA1004X=m
CONFIG_DVB_NXT6000=m
CONFIG_DVB_MT352=m
CONFIG_DVB_DIB3000MB=m
CONFIG_DVB_DIB3000MC=m
#
# DVB-C (cable) frontends
#
CONFIG_DVB_ATMEL_AT76C651=m
CONFIG_DVB_VES1820=m
CONFIG_DVB_TDA10021=m
CONFIG_DVB_STV0297=m
#
# ATSC (North American/Korean Terresterial DTV) frontends
#
# CONFIG_DVB_NXT2002 is not set
# CONFIG_DVB_OR51211 is not set
# CONFIG_DVB_OR51132 is not set
# CONFIG_DVB_BCM3510 is not set
# CONFIG_DVB_LGDT3302 is not set
CONFIG_VIDEO_SAA7146=m
CONFIG_VIDEO_SAA7146_VV=m
CONFIG_VIDEO_VIDEOBUF=m
CONFIG_VIDEO_BUF=m
#
# Graphics support
#
CONFIG_FB=y
CONFIG_FB_CFB_FILLRECT=m
CONFIG_FB_CFB_COPYAREA=m
CONFIG_FB_CFB_IMAGEBLIT=m
CONFIG_FB_SOFT_CURSOR=m
# CONFIG_FB_MACMODES is not set
CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_TILEBLITTING is not set
# CONFIG_FB_CIRRUS is not set
# CONFIG_FB_PM2 is not set
# CONFIG_FB_CYBER2000 is not set
# CONFIG_FB_ARC is not set
# CONFIG_FB_ASILIANT is not set
# CONFIG_FB_IMSTT is not set
# CONFIG_FB_VGA16 is not set
# CONFIG_FB_VESA is not set
CONFIG_VIDEO_SELECT=y
# CONFIG_FB_HGA is not set
# CONFIG_FB_NVIDIA is not set
# CONFIG_FB_RIVA is not set
# CONFIG_FB_I810 is not set
# CONFIG_FB_INTEL is not set
# CONFIG_FB_MATROX is not set
# CONFIG_FB_RADEON_OLD is not set
CONFIG_FB_RADEON=m
CONFIG_FB_RADEON_I2C=y
# CONFIG_FB_RADEON_DEBUG is not set
CONFIG_FB_ATY128=m
CONFIG_FB_ATY=m
# CONFIG_FB_ATY_CT is not set
# CONFIG_FB_ATY_GX is not set
# CONFIG_FB_SAVAGE is not set
# CONFIG_FB_SIS is not set
# CONFIG_FB_NEOMAGIC is not set
# CONFIG_FB_KYRO is not set
# CONFIG_FB_3DFX is not set
# CONFIG_FB_VOODOO1 is not set
# CONFIG_FB_TRIDENT is not set
# CONFIG_FB_GEODE is not set
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_VIRTUAL is not set
#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
# CONFIG_MDA_CONSOLE is not set
CONFIG_DUMMY_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE is not set
#
# Logo configuration
#
# CONFIG_LOGO is not set
# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
#
# Sound
#
CONFIG_SOUND=m
#
# Advanced Linux Sound Architecture
#
CONFIG_SND=m
CONFIG_SND_TIMER=m
CONFIG_SND_PCM=m
CONFIG_SND_HWDEP=m
CONFIG_SND_RAWMIDI=m
CONFIG_SND_SEQUENCER=m
CONFIG_SND_SEQ_DUMMY=m
CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=m
CONFIG_SND_PCM_OSS=m
# CONFIG_SND_SEQUENCER_OSS is not set
CONFIG_SND_RTCTIMER=m
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
#
# Generic devices
#
CONFIG_SND_MPU401_UART=m
CONFIG_SND_OPL3_LIB=m
# CONFIG_SND_DUMMY is not set
# CONFIG_SND_VIRMIDI is not set
# CONFIG_SND_MTPAV is not set
# CONFIG_SND_SERIAL_U16550 is not set
# CONFIG_SND_MPU401 is not set
#
# ISA devices
#
# CONFIG_SND_AD1816A is not set
# CONFIG_SND_AD1848 is not set
# CONFIG_SND_CS4231 is not set
# CONFIG_SND_CS4232 is not set
# CONFIG_SND_CS4236 is not set
# CONFIG_SND_ES968 is not set
# CONFIG_SND_ES1688 is not set
# CONFIG_SND_ES18XX is not set
# CONFIG_SND_GUSCLASSIC is not set
# CONFIG_SND_GUSEXTREME is not set
# CONFIG_SND_GUSMAX is not set
# CONFIG_SND_INTERWAVE is not set
# CONFIG_SND_INTERWAVE_STB is not set
# CONFIG_SND_OPTI92X_AD1848 is not set
# CONFIG_SND_OPTI92X_CS4231 is not set
# CONFIG_SND_OPTI93X is not set
# CONFIG_SND_SB8 is not set
# CONFIG_SND_SB16 is not set
# CONFIG_SND_SBAWE is not set
# CONFIG_SND_WAVEFRONT is not set
# CONFIG_SND_ALS100 is not set
# CONFIG_SND_AZT2320 is not set
# CONFIG_SND_CMI8330 is not set
# CONFIG_SND_DT019X is not set
# CONFIG_SND_OPL3SA2 is not set
# CONFIG_SND_SGALAXY is not set
# CONFIG_SND_SSCAPE is not set
#
# PCI devices
#
CONFIG_SND_AC97_CODEC=m
# CONFIG_SND_ALI5451 is not set
# CONFIG_SND_ATIIXP is not set
# CONFIG_SND_ATIIXP_MODEM is not set
# CONFIG_SND_AU8810 is not set
# CONFIG_SND_AU8820 is not set
# CONFIG_SND_AU8830 is not set
# CONFIG_SND_AZT3328 is not set
# CONFIG_SND_BT87X is not set
# CONFIG_SND_CS46XX is not set
# CONFIG_SND_CS4281 is not set
# CONFIG_SND_EMU10K1 is not set
# CONFIG_SND_EMU10K1X is not set
# CONFIG_SND_CA0106 is not set
# CONFIG_SND_KORG1212 is not set
# CONFIG_SND_MIXART is not set
# CONFIG_SND_NM256 is not set
# CONFIG_SND_RME32 is not set
# CONFIG_SND_RME96 is not set
# CONFIG_SND_RME9652 is not set
# CONFIG_SND_HDSP is not set
# CONFIG_SND_HDSPM is not set
# CONFIG_SND_TRIDENT is not set
CONFIG_SND_YMFPCI=m
# CONFIG_SND_ALS4000 is not set
# CONFIG_SND_CMIPCI is not set
CONFIG_SND_ENS1370=m
CONFIG_SND_ENS1371=m
# CONFIG_SND_ES1938 is not set
# CONFIG_SND_ES1968 is not set
# CONFIG_SND_MAESTRO3 is not set
# CONFIG_SND_FM801 is not set
# CONFIG_SND_ICE1712 is not set
# CONFIG_SND_ICE1724 is not set
# CONFIG_SND_INTEL8X0 is not set
# CONFIG_SND_INTEL8X0M is not set
# CONFIG_SND_SONICVIBES is not set
# CONFIG_SND_VIA82XX is not set
# CONFIG_SND_VIA82XX_MODEM is not set
# CONFIG_SND_VX222 is not set
# CONFIG_SND_HDA_INTEL is not set
#
# Open Sound System
#
# CONFIG_SOUND_PRIME is not set
#
# USB support
#
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
# CONFIG_USB is not set
#
# USB Gadget Support
#
# CONFIG_USB_GADGET is not set
#
# MMC/SD Card support
#
# CONFIG_MMC is not set
#
# InfiniBand support
#
# CONFIG_INFINIBAND is not set
#
# SN Devices
#
#
# File systems
#
# CONFIG_EXT2_FS is not set
CONFIG_EXT3_FS=y
CONFIG_EXT3_FS_XATTR=y
CONFIG_EXT3_FS_POSIX_ACL=y
# CONFIG_EXT3_FS_SECURITY is not set
CONFIG_JBD=y
# CONFIG_JBD_DEBUG is not set
CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
CONFIG_FS_POSIX_ACL=y
#
# XFS support
#
# CONFIG_XFS_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_ROMFS_FS is not set
CONFIG_INOTIFY=y
CONFIG_QUOTA=y
# CONFIG_QFMT_V1 is not set
# CONFIG_QFMT_V2 is not set
CONFIG_QUOTACTL=y
CONFIG_DNOTIFY=y
# CONFIG_AUTOFS_FS is not set
CONFIG_AUTOFS4_FS=y
#
# CD-ROM/DVD Filesystems
#
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
# CONFIG_ZISOFS is not set
CONFIG_UDF_FS=m
CONFIG_UDF_NLS=y
#
# DOS/FAT/NT Filesystems
#
CONFIG_FAT_FS=m
# CONFIG_MSDOS_FS is not set
CONFIG_VFAT_FS=m
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# CONFIG_NTFS_FS is not set
#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
CONFIG_SYSFS=y
# CONFIG_DEVPTS_FS_XATTR is not set
CONFIG_TMPFS=y
# CONFIG_TMPFS_XATTR is not set
# CONFIG_HUGETLBFS is not set
# CONFIG_HUGETLB_PAGE is not set
CONFIG_RAMFS=y
#
# Miscellaneous filesystems
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
#
# Network File Systems
#
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_NFS_V3_ACL=y
# CONFIG_NFS_V4 is not set
# CONFIG_NFS_DIRECTIO is not set
CONFIG_NFSD=m
CONFIG_NFSD_V2_ACL=y
CONFIG_NFSD_V3=y
CONFIG_NFSD_V3_ACL=y
# CONFIG_NFSD_V4 is not set
CONFIG_NFSD_TCP=y
CONFIG_ROOT_NFS=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_EXPORTFS=m
CONFIG_NFS_ACL_SUPPORT=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
CONFIG_SUNRPC_GSS=m
CONFIG_RPCSEC_GSS_KRB5=m
CONFIG_RPCSEC_GSS_SPKM3=m
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
#
# Native Language Support
#
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=m
CONFIG_NLS_CODEPAGE_737=m
CONFIG_NLS_CODEPAGE_775=m
CONFIG_NLS_CODEPAGE_850=m
CONFIG_NLS_CODEPAGE_852=m
CONFIG_NLS_CODEPAGE_855=m
CONFIG_NLS_CODEPAGE_857=m
CONFIG_NLS_CODEPAGE_860=m
CONFIG_NLS_CODEPAGE_861=m
CONFIG_NLS_CODEPAGE_862=m
CONFIG_NLS_CODEPAGE_863=m
CONFIG_NLS_CODEPAGE_864=m
CONFIG_NLS_CODEPAGE_865=m
CONFIG_NLS_CODEPAGE_866=m
CONFIG_NLS_CODEPAGE_869=m
CONFIG_NLS_CODEPAGE_936=m
CONFIG_NLS_CODEPAGE_950=m
CONFIG_NLS_CODEPAGE_932=m
CONFIG_NLS_CODEPAGE_949=m
CONFIG_NLS_CODEPAGE_874=m
CONFIG_NLS_ISO8859_8=m
CONFIG_NLS_CODEPAGE_1250=m
CONFIG_NLS_CODEPAGE_1251=m
# CONFIG_NLS_ASCII is not set
CONFIG_NLS_ISO8859_1=m
CONFIG_NLS_ISO8859_2=m
CONFIG_NLS_ISO8859_3=m
CONFIG_NLS_ISO8859_4=m
CONFIG_NLS_ISO8859_5=m
CONFIG_NLS_ISO8859_6=m
CONFIG_NLS_ISO8859_7=m
CONFIG_NLS_ISO8859_9=m
CONFIG_NLS_ISO8859_13=m
CONFIG_NLS_ISO8859_14=m
CONFIG_NLS_ISO8859_15=m
CONFIG_NLS_KOI8_R=m
CONFIG_NLS_KOI8_U=m
CONFIG_NLS_UTF8=m
#
# Profiling support
#
# CONFIG_PROFILING is not set
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_DEBUG_KERNEL=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_LOG_BUF_SHIFT=14
# CONFIG_SCHEDSTATS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_KOBJECT is not set
# CONFIG_DEBUG_HIGHMEM is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_FS is not set
# CONFIG_FRAME_POINTER is not set
CONFIG_EARLY_PRINTK=y
# CONFIG_DEBUG_STACKOVERFLOW is not set
# CONFIG_KPROBES is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_DEBUG_PAGEALLOC is not set
CONFIG_4KSTACKS=y
CONFIG_X86_FIND_SMP_CONFIG=y
CONFIG_X86_MPPARSE=y
#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
#
# Cryptographic options
#
CONFIG_CRYPTO=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=y
# CONFIG_CRYPTO_WP512 is not set
# CONFIG_CRYPTO_TGR192 is not set
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_BLOWFISH=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_SERPENT=y
CONFIG_CRYPTO_AES_586=y
CONFIG_CRYPTO_CAST5=y
CONFIG_CRYPTO_CAST6=y
CONFIG_CRYPTO_TEA=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_KHAZAD=y
# CONFIG_CRYPTO_ANUBIS is not set
CONFIG_CRYPTO_DEFLATE=y
# CONFIG_CRYPTO_MICHAEL_MIC is not set
# CONFIG_CRYPTO_CRC32C is not set
# CONFIG_CRYPTO_TEST is not set
#
# Hardware crypto devices
#
# CONFIG_CRYPTO_DEV_PADLOCK is not set
#
# Library routines
#
# CONFIG_CRC_CCITT is not set
CONFIG_CRC32=m
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_GENERIC_HARDIRQS=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_X86_BIOS_REBOOT=y
CONFIG_PC=y
--
Antti Salmela
^ permalink raw reply
* Re: Netlink connector
From: Harald Welte @ 2005-07-26 8:45 UTC (permalink / raw)
To: Evgeniy Polyakov
Cc: Andrew Morton, Eric Leblond, netdev, netfilter-devel,
linux-kernel, Patrick McHardy
In-Reply-To: <20050725193351.GB30567@2ka.mipt.ru>
[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]
On Mon, Jul 25, 2005 at 11:33:51PM +0400, Evgeniy Polyakov wrote:
> Netlink is transport protocol - no need to add complexity into it,
> it must be as simple as possible and thus extensible.
yes. but when you run into a serious addressing shortage (like the
internet does with ipv4), you develop something that provides more
addresses (such as ipv6). That's why support for more groups than 32
(per family) is something that should be put in the netlink protocol.
I totally agree that we need a higher-level api on top of that, in order
to hide the details of the networking stack for those not interested in
it.
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: Re: drop counts
From: P @ 2005-07-26 8:36 UTC (permalink / raw)
To: Jesse Brandeburg; +Cc: netdev, e1000-devel
In-Reply-To: <Pine.WNT.4.63.0507251041150.832@jbrandeb-desk.amr.corp.intel.com>
Jesse Brandeburg wrote:
> On Thu, 21 Jul 2005, P@draigBrady.com wrote:
>
>> In 2.6.12 this changed to:
>>
>> packets = gprc
>> dropped = mpc
>> fifo = mpc
>> missed = mpc
>>
>> I think there still is a problem. Notice that /proc/net/dev
>> reports rx_dropped + rx_missed_errors for the drop column,
>> and so will be double counting drops currently.
>> Therefore I think we should be doing:
>>
>> packets = gprc
>> dropped = 0
>> fifo = rnbc
>> missed = mpc
>
>
> Double counting drops may be an issue, and I believe we should make that
> change.
great.
> but I don't think its the right thing to report rnbc in fifo
> errors (there was actually no error).
>
>> I'm putting rnbc in fifo as it's not accumulated into
>> anything else in the kernel. Also it's good to have
>> it reported as one can see when there is pressure on
>> the e1000 driver buffer. This is useful for tuning its size
>> to protect against drops and latency.
>
>
> Whenever I want the RNBC numbers I simply look into ethtool -S eth0, and
> that allows me to tune. I see no reason to "reuse" the fifo errors stat
> with misleading data.
Fair enough if rnbc is actually reported.
ethtool -S doesn't report it with my driver,
but I see this stat has been added to the
latest driver at least.
So I agree on both points.
thanks!
--
Pádraig Brady - http://www.pixelbeat.org
--
-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. http://ads.osdn.com/?ad_idt77&alloc_id\x16492&op=click
^ permalink raw reply
* Re: Netlink connector
From: Evgeniy Polyakov @ 2005-07-26 6:31 UTC (permalink / raw)
To: Thomas Graf
Cc: Andrew Morton, Harald Welte, netdev, netfilter-devel,
linux-kernel, Patrick McHardy
In-Reply-To: <20050726061447.GZ10481@postel.suug.ch>
On Tue, Jul 26, 2005 at 08:14:47AM +0200, Thomas Graf (tgraf@suug.ch) wrote:
> * Evgeniy Polyakov <20050726044547.GA32006@2ka.mipt.ru> 2005-07-26 08:45
> > On Tue, Jul 26, 2005 at 01:46:04AM +0200, Patrick McHardy (kaber@trash.net) wrote:
> > > Usually netlink is easily extendable by using nested TLVs. By hiding
> > > this you basically remove this extensibility.
> >
> > Current netlink is not extensible for _many_ different users.
>
> Patrick's key point was that by hiding some of the functionality
> you remove a lot of the flexbility.
>
> > It has only 32 sockets.
>
> You mean MAX_LINKS? That is the current number of reserved
> netlink protocols. The ethertaps are obsolete and can be
> reused so we're currently using 16 out of 256 possible
> protocols. If that is not enough there are ways to work
> around this. However, I also see a need for a generic protocol
> providing a simplified interface for small applications.
> Nevertheless we should take the time and work things out on
> the netlink level first, netlink has issues and we should not
> work around them in a upper layer.
>
> > > But my main objection is that it sends everything to userspace even
> > > if noone is listening. This can't be used for things that generate
> > > lots of events, and also will get problematic is the number of users
> > > increases.
> >
> > It is a problem for existing netlink - either check in bind time,
> > what could be done for connector, or in socket creation time.
>
> No, I think you are misunderstanding something. As I said, we can
> easly add a function netlink_nr_subscribers(sk, groups) so the
> check can be done before starting to build the message. This is
> no problem, it simply didn't make sense so far because netlink
> event messages were mostly used for rare events.
Yep.
> > Actually it is not even a problem, since checking is being done,
> > but after allocation and message filling, such check can be moved into
> > cn_netlink_send() in connector, but different netlink users,
> > who prefers to use different sockets, must perform it by itself in each
> > place, where skb is allocated...
>
> Sure, which is the right thing, it makes perfect sense to check
> before starting the process of building and event and sending it.
>
> > Connector is a solution for current situation,
> > it can be deployed with few casualties.
>
> The problem is that netlink is likely to change in order
> to cope with some recent needs, e.g. ctnetlink but also other
> current issues which need to be addressed. Therefore I suggest
> to build connector on top of the updated netlink so you we have
> one thing less to worry about when thinking about compatibility.
>
> > Creating a new netlink2 socket for device, which wants to replace ioctl
> > controlling or broadcast it's state is a wrong way.
>
> Slowly, we might need netlink2 _in case_ we cannot work things
> out without breaking compatibility. This has nothing to do with
> the connector, there are netlink users which have new needs such
> as more groups, at least some of them need the flexibility of
> netlink itself so we have to work things out for them.
>
> > Different sockets/flows does not allow easy flow control.
>
> I'm not sure what you mean.
Concider socket overrun - message will be dropped,
using special flags in connector [it's size field was selected to be 4
bytes, and thus has big reserve] this subsystem can requeue message
later after timeout or something similar...
> > We have one pipe - ethernet, and many protocols inside this pipe
> > with different headers - it is the same here - netlink is such a pipe,
> > and with connector it allows to have different protocols in it.
>
> At least parts of your connector is just a redudant implementation
> of what netlink is already capable of doing. Sure, some of them
> have issues but there is no reason to just build a new protocol on
> top of another one if the protocol beneath has issues which can be
> resolved.
>
> > > You still have to take care of mixed 64/32 bit environments, u64 fields
> > > for example are differently alligned.
> >
> > Connector has a size in it's header - ioctl does not.
>
> You have exactly the same issues as netlink as soon as you transfer
> structs, believe it or not.
>
> > It does not "fix" the "problem" of skb management knowledge, which I
> > described.
>
> Yes ok, this is a different issue and as Patrick stated already
> those have been mostly worked out by providing a new set of
> macros. Except for a few leftovers, which will be addressed, there
> is no need to call skb functions anymore. The reason the plain
> skb interface was used is simply that the authors of most of the
> netlink using code are in fact very familiar with the skb interface,
> that's it.
I saw your changes - theay are very usefull, but _only_ for sending
part. Kernel receiver still needs dequeuing, freeing and NLKMSG macros.
In first netlink days it also needed skb_recv_msg() or something
similar...
> > > You can still built this stuff on top, but the workarounds for netlink
> > > limitations need to be fixed in netlink.
> >
> > I could not call it workaround, I think it is a management layer,
> > which allows :
>
> Listen, nobody wants to take away your baby. ;-> There are some
Yeah :)
> objections of things which would rather be fixed in the netlink
> layer first and the remaining part that is missing goes into the
> connector. I see a lot of replicated netlink code in the connector
> which is no necessary. I perfectly agree with you that we require
> some form of simplified addressing and easier message handling
> for simple applications but just building another layer on top
> of netlink without respecting the capabilities of netlink itself
> is not the way to go as I see it. For example, we'll probably add
> a new group subscription mechanism to netlink which might perfectly
> suit the needs of your connector.
That is why I raise this question again and againg to see, what ideas should
be moved from connector into netlink and vice versa... :)
> > 1. easy usage. Just register a callback and that is all. Callback will
> > be invoced each time new message arrives. No need to
> > dequeue/free/anything.
>
> Good point, also doable in netlink directly. Just get rid of the
> usual family_rcv -> family_rcv_skb -> family_rcv_msg process and
> do a callback registration interface instead. However, often the
> processing of a message and the resulting ack must be done as an
> atomic operation, e.g. rtnetlink.
It also better to move into workqueue - just to be sure users will not
do some wrong things...
> > 2. easy usage. Call one function for message delivering, which can
> > care of nonexistent users, perform flow control, congestion control,
> > guarantee delivery and any other.
>
> I don't understand what exactly you mean but netlink itself
> is not reliable under memory pressure.
Connector has cn_netlink_send() which is a wrapper on top
of skb allocation, queuing and so on.
by flow/congestion control I mean here, that this function
can check for remote peer existing, requeue message if socket
overrun is caught, guarantee that no OOM condition was caught, and
requeue if it was the case and so on.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Netlink connector
From: Thomas Graf @ 2005-07-26 6:14 UTC (permalink / raw)
To: Evgeniy Polyakov
Cc: Andrew Morton, Harald Welte, netdev, netfilter-devel,
linux-kernel, Patrick McHardy
In-Reply-To: <20050726044547.GA32006@2ka.mipt.ru>
* Evgeniy Polyakov <20050726044547.GA32006@2ka.mipt.ru> 2005-07-26 08:45
> On Tue, Jul 26, 2005 at 01:46:04AM +0200, Patrick McHardy (kaber@trash.net) wrote:
> > Usually netlink is easily extendable by using nested TLVs. By hiding
> > this you basically remove this extensibility.
>
> Current netlink is not extensible for _many_ different users.
Patrick's key point was that by hiding some of the functionality
you remove a lot of the flexbility.
> It has only 32 sockets.
You mean MAX_LINKS? That is the current number of reserved
netlink protocols. The ethertaps are obsolete and can be
reused so we're currently using 16 out of 256 possible
protocols. If that is not enough there are ways to work
around this. However, I also see a need for a generic protocol
providing a simplified interface for small applications.
Nevertheless we should take the time and work things out on
the netlink level first, netlink has issues and we should not
work around them in a upper layer.
> > But my main objection is that it sends everything to userspace even
> > if noone is listening. This can't be used for things that generate
> > lots of events, and also will get problematic is the number of users
> > increases.
>
> It is a problem for existing netlink - either check in bind time,
> what could be done for connector, or in socket creation time.
No, I think you are misunderstanding something. As I said, we can
easly add a function netlink_nr_subscribers(sk, groups) so the
check can be done before starting to build the message. This is
no problem, it simply didn't make sense so far because netlink
event messages were mostly used for rare events.
> Actually it is not even a problem, since checking is being done,
> but after allocation and message filling, such check can be moved into
> cn_netlink_send() in connector, but different netlink users,
> who prefers to use different sockets, must perform it by itself in each
> place, where skb is allocated...
Sure, which is the right thing, it makes perfect sense to check
before starting the process of building and event and sending it.
> Connector is a solution for current situation,
> it can be deployed with few casualties.
The problem is that netlink is likely to change in order
to cope with some recent needs, e.g. ctnetlink but also other
current issues which need to be addressed. Therefore I suggest
to build connector on top of the updated netlink so you we have
one thing less to worry about when thinking about compatibility.
> Creating a new netlink2 socket for device, which wants to replace ioctl
> controlling or broadcast it's state is a wrong way.
Slowly, we might need netlink2 _in case_ we cannot work things
out without breaking compatibility. This has nothing to do with
the connector, there are netlink users which have new needs such
as more groups, at least some of them need the flexibility of
netlink itself so we have to work things out for them.
> Different sockets/flows does not allow easy flow control.
I'm not sure what you mean.
> We have one pipe - ethernet, and many protocols inside this pipe
> with different headers - it is the same here - netlink is such a pipe,
> and with connector it allows to have different protocols in it.
At least parts of your connector is just a redudant implementation
of what netlink is already capable of doing. Sure, some of them
have issues but there is no reason to just build a new protocol on
top of another one if the protocol beneath has issues which can be
resolved.
> > You still have to take care of mixed 64/32 bit environments, u64 fields
> > for example are differently alligned.
>
> Connector has a size in it's header - ioctl does not.
You have exactly the same issues as netlink as soon as you transfer
structs, believe it or not.
> It does not "fix" the "problem" of skb management knowledge, which I
> described.
Yes ok, this is a different issue and as Patrick stated already
those have been mostly worked out by providing a new set of
macros. Except for a few leftovers, which will be addressed, there
is no need to call skb functions anymore. The reason the plain
skb interface was used is simply that the authors of most of the
netlink using code are in fact very familiar with the skb interface,
that's it.
> > You can still built this stuff on top, but the workarounds for netlink
> > limitations need to be fixed in netlink.
>
> I could not call it workaround, I think it is a management layer,
> which allows :
Listen, nobody wants to take away your baby. ;-> There are some
objections of things which would rather be fixed in the netlink
layer first and the remaining part that is missing goes into the
connector. I see a lot of replicated netlink code in the connector
which is no necessary. I perfectly agree with you that we require
some form of simplified addressing and easier message handling
for simple applications but just building another layer on top
of netlink without respecting the capabilities of netlink itself
is not the way to go as I see it. For example, we'll probably add
a new group subscription mechanism to netlink which might perfectly
suit the needs of your connector.
> 1. easy usage. Just register a callback and that is all. Callback will
> be invoced each time new message arrives. No need to
> dequeue/free/anything.
Good point, also doable in netlink directly. Just get rid of the
usual family_rcv -> family_rcv_skb -> family_rcv_msg process and
do a callback registration interface instead. However, often the
processing of a message and the resulting ack must be done as an
atomic operation, e.g. rtnetlink.
> 2. easy usage. Call one function for message delivering, which can
> care of nonexistent users, perform flow control, congestion control,
> guarantee delivery and any other.
I don't understand what exactly you mean but netlink itself
is not reliable under memory pressure.
^ permalink raw reply
* Re: Netlink connector
From: Evgeniy Polyakov @ 2005-07-26 5:01 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Andrew Morton, Harald Welte, netdev, netfilter-devel,
linux-kernel, Patrick McHardy
In-Reply-To: <42E5C298.8010209@osdl.org>
On Mon, Jul 25, 2005 at 09:56:56PM -0700, Stephen Hemminger (shemminger@osdl.org) wrote:
> Evgeniy Polyakov wrote:
>
> >On Tue, Jul 26, 2005 at 01:46:04AM +0200, Patrick McHardy
> >(kaber@trash.net) wrote:
> >
> >
> >>Evgeniy Polyakov wrote:
> >>
> >>
> >>>On Mon, Jul 25, 2005 at 04:32:32PM +0200, Patrick McHardy
> >>>(kaber@trash.net) wrote:
> >>>
> >>>
> >>>
> >>>>If I understand correctly it tries to workaround some netlink
> >>>>limitations (limited number of netlink families and multicast groups)
> >>>>by sending everything to userspace and demultiplexing it there.
> >>>>Same in the other direction, an additional layer on top of netlink
> >>>>does basically the same thing netlink already does. This looks like
> >>>>a step in the wrong direction to me, netlink should instead be fixed
> >>>>to support what is needed.
> >>>>
> >>>>
> >>>Not only it.
> >>>The main _first_ idea was to simplify userspace mesasge handling as much
> >>>as possible.
> >>>In first releases I called it ioctl-ng - any module that want ot
> >>>communicate with userspace in the way ioctl does,
> >>>
> >>>
> >>Usually netlink is easily extendable by using nested TLVs. By hiding
> >>this you basically remove this extensibility.
> >>
> >>
> >
> >Current netlink is not extensible for _many_ different users.
> >It has only 32 sockets.
> >
> >
> >
> >>>requires skb allocation/freeing/handling.
> >>>Does RTC driver writer need to know what is the difference between
> >>>shared and cloned skb? Should kernel user of such message bus
> >>>have to know about skb at all?
> >>>
> >>>
> >>Netlink users don't have to care about shared or cloned skbs. I don't
> >>think its a big issue to use alloc_skb and then the usual netlink
> >>macros. Thomas added a number of macros that simplfiy use a lot.
> >>
> >>
> >
> >Kernel user also must know about difference between unicast/broadcast,
> >how to dequeue the skb, how to free it and in what context.
> >ioctl users do not need to know how file_operations is bound to file.
> >
> >
> >
> >>But my main objection is that it sends everything to userspace even
> >>if noone is listening. This can't be used for things that generate
> >>lots of events, and also will get problematic is the number of users
> >>increases.
> >>
> >>
> >
> >It is a problem for existing netlink - either check in bind time,
> >what could be done for connector, or in socket creation time.
> >
> >Actually it is not even a problem, since checking is being done,
> >but after allocation and message filling, such check can be moved into
> >cn_netlink_send() in connector, but different netlink users,
> >who prefers to use different sockets, must perform it by itself in each
> >place, where skb is allocated...
> >
> >Connector is a solution for current situation,
> >it can be deployed with few casualties.
> >Creating a new netlink2 socket for device, which wants to replace ioctl
> >controlling or broadcast it's state is a wrong way.
> >Different sockets/flows does not allow easy flow control.
> >
> >We have one pipe - ethernet, and many protocols inside this pipe
> >with different headers - it is the same here - netlink is such a pipe,
> >and with connector it allows to have different protocols in it.
> >
> >
> >
> >>>With char device I only need to register my callback - with kernel
> >>>connector it is the same, but allows to use the whole power of netlink,
> >>>especially without nice ioctl features like different pointer size
> >>>in userspace and kernelspace.
> >>>
> >>>
> >>You still have to take care of mixed 64/32 bit environments, u64 fields
> >>for example are differently alligned.
> >>
> >>
> >
> >Connector has a size in it's header - ioctl does not.
> >
> >
> >
> >>>And number of free netlink sockets is _very_ small, especially
> >>>if allocate new one for simple notifications, which can be easily done
> >>>using connector.
> >>>
> >>>
> >>Then fix it so we can use more families and groups. I started some work
> >>on this, but I'm not sure if I have time to complete it.
> >>
> >>
> >
> >It does not "fix" the "problem" of skb management knowledge, which I
> >described.
> >Netlink is a transport protocol, some general logic must be created on
> >top of it, like it is done in TCP/IP.
> >
> >
> >
> >>>And netlink can be extended to support it - netlink is a transport
> >>>protocol, it should not care about higher layer message handling,
> >>>connector instead will deliver message to the end user in a very
> >>>convenient form.
> >>>
> >>>
> >>You can still built this stuff on top, but the workarounds for netlink
> >>limitations need to be fixed in netlink.
> >>
> >>
> >
> >I could not call it workaround, I think it is a management layer,
> >which allows :
> >1. easy usage. Just register a callback and that is all. Callback will
> >be invoced each time new message arrives. No need to
> >dequeue/free/anything.
> >2. easy usage. Call one function for message delivering, which can
> >care of nonexistent users, perform flow control, congestion control,
> >guarantee delivery and any other.
> >3. Easily deployable - current implementation is so simple, and it does
> >work with existing netlink.
> >4. It is logical level on top of transport protocol, it is UDP/IP over
> >ethernet :)
> >
> >
> >
> If it is a transport, then it should be in the kernel. Otherwise, it
> becomes painful
> for applications with multiple input sources. Think of
> epoll/poll/select and threads,
> doing the demultiplexing in user space would be a pain for applications
> and libraries.
It _is_ in the kernel - multiplexing is being done in a send time,
userspace does not receive messages for different ID's.
Currently it is done using netlink groups, and I would like to change
it, but conenctor layer itself will not be changed, so no application
will be changed - they bound before and will only bound after.
one socket, different groups.
Ok, now application bound to -1 group will receive all traffic, but I
posted proof-of-concept patch to remove such behaviour.
> The other way to go is to use something like dbus/hal and use a higher level
> application oriented interface. The problem with that approach, is it
> assumes
> every management app wants to drag in gnome..
No need to parse headers there.
When we read from UDP socket, we do not get headers - connector users
do not read netlink header, and it is possible to completely remove
even connector header, although I would like to have it - some kind of
HDRINCL option...
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Netlink connector
From: Stephen Hemminger @ 2005-07-26 4:56 UTC (permalink / raw)
To: Evgeniy Polyakov
Cc: Andrew Morton, Harald Welte, netdev, netfilter-devel,
linux-kernel, Patrick McHardy
In-Reply-To: <20050726044547.GA32006@2ka.mipt.ru>
Evgeniy Polyakov wrote:
>On Tue, Jul 26, 2005 at 01:46:04AM +0200, Patrick McHardy (kaber@trash.net) wrote:
>
>
>>Evgeniy Polyakov wrote:
>>
>>
>>>On Mon, Jul 25, 2005 at 04:32:32PM +0200, Patrick McHardy
>>>(kaber@trash.net) wrote:
>>>
>>>
>>>
>>>>If I understand correctly it tries to workaround some netlink
>>>>limitations (limited number of netlink families and multicast groups)
>>>>by sending everything to userspace and demultiplexing it there.
>>>>Same in the other direction, an additional layer on top of netlink
>>>>does basically the same thing netlink already does. This looks like
>>>>a step in the wrong direction to me, netlink should instead be fixed
>>>>to support what is needed.
>>>>
>>>>
>>>Not only it.
>>>The main _first_ idea was to simplify userspace mesasge handling as much
>>>as possible.
>>>In first releases I called it ioctl-ng - any module that want ot
>>>communicate with userspace in the way ioctl does,
>>>
>>>
>>Usually netlink is easily extendable by using nested TLVs. By hiding
>>this you basically remove this extensibility.
>>
>>
>
>Current netlink is not extensible for _many_ different users.
>It has only 32 sockets.
>
>
>
>>>requires skb allocation/freeing/handling.
>>>Does RTC driver writer need to know what is the difference between
>>>shared and cloned skb? Should kernel user of such message bus
>>>have to know about skb at all?
>>>
>>>
>>Netlink users don't have to care about shared or cloned skbs. I don't
>>think its a big issue to use alloc_skb and then the usual netlink
>>macros. Thomas added a number of macros that simplfiy use a lot.
>>
>>
>
>Kernel user also must know about difference between unicast/broadcast,
>how to dequeue the skb, how to free it and in what context.
>ioctl users do not need to know how file_operations is bound to file.
>
>
>
>>But my main objection is that it sends everything to userspace even
>>if noone is listening. This can't be used for things that generate
>>lots of events, and also will get problematic is the number of users
>>increases.
>>
>>
>
>It is a problem for existing netlink - either check in bind time,
>what could be done for connector, or in socket creation time.
>
>Actually it is not even a problem, since checking is being done,
>but after allocation and message filling, such check can be moved into
>cn_netlink_send() in connector, but different netlink users,
>who prefers to use different sockets, must perform it by itself in each
>place, where skb is allocated...
>
>Connector is a solution for current situation,
>it can be deployed with few casualties.
>Creating a new netlink2 socket for device, which wants to replace ioctl
>controlling or broadcast it's state is a wrong way.
>Different sockets/flows does not allow easy flow control.
>
>We have one pipe - ethernet, and many protocols inside this pipe
>with different headers - it is the same here - netlink is such a pipe,
>and with connector it allows to have different protocols in it.
>
>
>
>>>With char device I only need to register my callback - with kernel
>>>connector it is the same, but allows to use the whole power of netlink,
>>>especially without nice ioctl features like different pointer size
>>>in userspace and kernelspace.
>>>
>>>
>>You still have to take care of mixed 64/32 bit environments, u64 fields
>>for example are differently alligned.
>>
>>
>
>Connector has a size in it's header - ioctl does not.
>
>
>
>>>And number of free netlink sockets is _very_ small, especially
>>>if allocate new one for simple notifications, which can be easily done
>>>using connector.
>>>
>>>
>>Then fix it so we can use more families and groups. I started some work
>>on this, but I'm not sure if I have time to complete it.
>>
>>
>
>It does not "fix" the "problem" of skb management knowledge, which I
>described.
>Netlink is a transport protocol, some general logic must be created on
>top of it, like it is done in TCP/IP.
>
>
>
>>>And netlink can be extended to support it - netlink is a transport
>>>protocol, it should not care about higher layer message handling,
>>>connector instead will deliver message to the end user in a very
>>>convenient form.
>>>
>>>
>>You can still built this stuff on top, but the workarounds for netlink
>>limitations need to be fixed in netlink.
>>
>>
>
>I could not call it workaround, I think it is a management layer,
>which allows :
>1. easy usage. Just register a callback and that is all. Callback will
>be invoced each time new message arrives. No need to
>dequeue/free/anything.
>2. easy usage. Call one function for message delivering, which can
>care of nonexistent users, perform flow control, congestion control,
>guarantee delivery and any other.
>3. Easily deployable - current implementation is so simple, and it does
>work with existing netlink.
>4. It is logical level on top of transport protocol, it is UDP/IP over
>ethernet :)
>
>
>
If it is a transport, then it should be in the kernel. Otherwise, it
becomes painful
for applications with multiple input sources. Think of
epoll/poll/select and threads,
doing the demultiplexing in user space would be a pain for applications
and libraries.
The other way to go is to use something like dbus/hal and use a higher level
application oriented interface. The problem with that approach, is it
assumes
every management app wants to drag in gnome..
^ permalink raw reply
* Re: Netlink connector
From: Evgeniy Polyakov @ 2005-07-26 4:45 UTC (permalink / raw)
To: Patrick McHardy
Cc: Andrew Morton, Harald Welte, netdev, netfilter-devel,
linux-kernel
In-Reply-To: <42E579BC.8000701@trash.net>
On Tue, Jul 26, 2005 at 01:46:04AM +0200, Patrick McHardy (kaber@trash.net) wrote:
> Evgeniy Polyakov wrote:
> >On Mon, Jul 25, 2005 at 04:32:32PM +0200, Patrick McHardy
> >(kaber@trash.net) wrote:
> >
> >>If I understand correctly it tries to workaround some netlink
> >>limitations (limited number of netlink families and multicast groups)
> >>by sending everything to userspace and demultiplexing it there.
> >>Same in the other direction, an additional layer on top of netlink
> >>does basically the same thing netlink already does. This looks like
> >>a step in the wrong direction to me, netlink should instead be fixed
> >>to support what is needed.
> >
> >Not only it.
> >The main _first_ idea was to simplify userspace mesasge handling as much
> >as possible.
> >In first releases I called it ioctl-ng - any module that want ot
> >communicate with userspace in the way ioctl does,
>
> Usually netlink is easily extendable by using nested TLVs. By hiding
> this you basically remove this extensibility.
Current netlink is not extensible for _many_ different users.
It has only 32 sockets.
> >requires skb allocation/freeing/handling.
> >Does RTC driver writer need to know what is the difference between
> >shared and cloned skb? Should kernel user of such message bus
> >have to know about skb at all?
>
> Netlink users don't have to care about shared or cloned skbs. I don't
> think its a big issue to use alloc_skb and then the usual netlink
> macros. Thomas added a number of macros that simplfiy use a lot.
Kernel user also must know about difference between unicast/broadcast,
how to dequeue the skb, how to free it and in what context.
ioctl users do not need to know how file_operations is bound to file.
> But my main objection is that it sends everything to userspace even
> if noone is listening. This can't be used for things that generate
> lots of events, and also will get problematic is the number of users
> increases.
It is a problem for existing netlink - either check in bind time,
what could be done for connector, or in socket creation time.
Actually it is not even a problem, since checking is being done,
but after allocation and message filling, such check can be moved into
cn_netlink_send() in connector, but different netlink users,
who prefers to use different sockets, must perform it by itself in each
place, where skb is allocated...
Connector is a solution for current situation,
it can be deployed with few casualties.
Creating a new netlink2 socket for device, which wants to replace ioctl
controlling or broadcast it's state is a wrong way.
Different sockets/flows does not allow easy flow control.
We have one pipe - ethernet, and many protocols inside this pipe
with different headers - it is the same here - netlink is such a pipe,
and with connector it allows to have different protocols in it.
> >With char device I only need to register my callback - with kernel
> >connector it is the same, but allows to use the whole power of netlink,
> >especially without nice ioctl features like different pointer size
> >in userspace and kernelspace.
>
> You still have to take care of mixed 64/32 bit environments, u64 fields
> for example are differently alligned.
Connector has a size in it's header - ioctl does not.
> >And number of free netlink sockets is _very_ small, especially
> >if allocate new one for simple notifications, which can be easily done
> >using connector.
>
> Then fix it so we can use more families and groups. I started some work
> on this, but I'm not sure if I have time to complete it.
It does not "fix" the "problem" of skb management knowledge, which I
described.
Netlink is a transport protocol, some general logic must be created on
top of it, like it is done in TCP/IP.
> >And netlink can be extended to support it - netlink is a transport
> >protocol, it should not care about higher layer message handling,
> >connector instead will deliver message to the end user in a very
> >convenient form.
>
> You can still built this stuff on top, but the workarounds for netlink
> limitations need to be fixed in netlink.
I could not call it workaround, I think it is a management layer,
which allows :
1. easy usage. Just register a callback and that is all. Callback will
be invoced each time new message arrives. No need to
dequeue/free/anything.
2. easy usage. Call one function for message delivering, which can
care of nonexistent users, perform flow control, congestion control,
guarantee delivery and any other.
3. Easily deployable - current implementation is so simple, and it does
work with existing netlink.
4. It is logical level on top of transport protocol, it is UDP/IP over
ethernet :)
> >P.S. I've removed netdev@redhat.com - please do not add subscribers-only
> >private mail lists.
>
> Wasn't me :)
Yep :)
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Netlink connector
From: Thomas Graf @ 2005-07-26 0:30 UTC (permalink / raw)
To: Patrick McHardy
Cc: Evgeniy Polyakov, Andrew Morton, Harald Welte, netdev,
netfilter-devel, Jamal Hadi Salim, linux-kernel
In-Reply-To: <42E580CF.4010800@trash.net>
* Patrick McHardy <42E580CF.4010800@trash.net> 2005-07-26 02:16
> Thomas Graf wrote:
> >* Patrick McHardy <42E579BC.8000701@trash.net> 2005-07-26 01:46
> >
> >>You still have to take care of mixed 64/32 bit environments, u64 fields
> >>for example are differently alligned.
> >
> >My solution to this (in the same patchset) is that we never
> >derference u64s but instead copy them.
>
> I don't understand. The problem is mainly u64 embedded in structures,
> the structs have different sizes if the u64 is not 8 byte aligned
> and the structure size padded to a multiple of 8.
Like in gnet_stats, yes. I thought you meant usages like *(u64 *)
which we shouldn't do either.
> I started working on it after the OLS party, so no postable code yet :)
> The idea for more groups is basically to remove the fixed groups
> bitmask from struct sockaddr_nl and use setsockopt to add/remove
> multicast subscriptions. If we add the limitation that a packet
> can only be multicasted to a single group we can support an arbitary
> number of groups, otherwise we would still be limited by size of
> skb->cb.
I was thinking of subscription messages over netlink itself for
the advantage that we could use it within the distributed netlink
protocol that has to come up sometime soon. Well, both ways
are ok I guess, the ease of distributive usage is my only argument.
> This limitation shouldn't be a problem, AFAIK nothing is
> multicasting to multiple groups at once right now and the increased
> number of groups will allow a better granularity anyway.
I'm not aware of any and I agree. We don't need n<->n subscriptions,
1<->n is perfectly fine as I see it.
> The main
> problem is keeping it backwards-compatible for current netlink users.
> If this isn't possible we may need to call it netlink2.
I think Jamal has a moral patent on the name netlink2 so be careful ;->
It should be possible to remain compatible, I don't see any
unresolveable issues right now.
^ permalink raw reply
* Re: Netlink connector
From: Patrick McHardy @ 2005-07-26 0:16 UTC (permalink / raw)
To: Thomas Graf
Cc: Evgeniy Polyakov, Andrew Morton, Harald Welte, netdev,
netfilter-devel, linux-kernel
In-Reply-To: <20050725235626.GX10481@postel.suug.ch>
Thomas Graf wrote:
> * Patrick McHardy <42E579BC.8000701@trash.net> 2005-07-26 01:46
>
>>You still have to take care of mixed 64/32 bit environments, u64 fields
>>for example are differently alligned.
>
> My solution to this (in the same patchset) is that we never
> derference u64s but instead copy them.
I don't understand. The problem is mainly u64 embedded in structures,
the structs have different sizes if the u64 is not 8 byte aligned
and the structure size padded to a multiple of 8.
>>Then fix it so we can use more families and groups. I started some work
>>on this, but I'm not sure if I have time to complete it.
>
> Great, this is one of the remaining issues I haven't solved yet.
> If you want me to take over just hand over your unfinished work
> and I'll integrate it into my patchset.
I started working on it after the OLS party, so no postable code yet :)
The idea for more groups is basically to remove the fixed groups
bitmask from struct sockaddr_nl and use setsockopt to add/remove
multicast subscriptions. If we add the limitation that a packet
can only be multicasted to a single group we can support an arbitary
number of groups, otherwise we would still be limited by size of
skb->cb. This limitation shouldn't be a problem, AFAIK nothing is
multicasting to multiple groups at once right now and the increased
number of groups will allow a better granularity anyway. The main
problem is keeping it backwards-compatible for current netlink users.
If this isn't possible we may need to call it netlink2.
Regards
Patrick
^ permalink raw reply
* Re: Netlink connector
From: Thomas Graf @ 2005-07-25 23:56 UTC (permalink / raw)
To: Patrick McHardy
Cc: Evgeniy Polyakov, Andrew Morton, Harald Welte, netdev,
netfilter-devel, linux-kernel
In-Reply-To: <42E579BC.8000701@trash.net>
* Patrick McHardy <42E579BC.8000701@trash.net> 2005-07-26 01:46
> Netlink users don't have to care about shared or cloned skbs. I don't
> think its a big issue to use alloc_skb and then the usual netlink
> macros. Thomas added a number of macros that simplfiy use a lot.
Once I've finished the generic netlink attribute macros the
usage will be even simpler. I wrote down all the things I want
to do today in a park and I intend to write the code once I'm
back from my vacation.
> But my main objection is that it sends everything to userspace even
> if noone is listening. This can't be used for things that generate
> lots of events, and also will get problematic is the number of users
> increases.
My patches will include a new function netlink_nr_subscribers()
taking the socket and a mask of groups. I posted something
simliar during an earlier connector discussion already.
> You still have to take care of mixed 64/32 bit environments, u64 fields
> for example are differently alligned.
My solution to this (in the same patchset) is that we never
derference u64s but instead copy them.
> Then fix it so we can use more families and groups. I started some work
> on this, but I'm not sure if I have time to complete it.
Great, this is one of the remaining issues I haven't solved yet.
If you want me to take over just hand over your unfinished work
and I'll integrate it into my patchset.
I'm sorry to not being able to provide any code yet, it's
one of the first things I'll do once I'm back.
^ permalink raw reply
* Re: Netlink connector
From: Patrick McHardy @ 2005-07-25 23:46 UTC (permalink / raw)
To: Evgeniy Polyakov
Cc: Andrew Morton, Harald Welte, netdev, netfilter-devel,
linux-kernel
In-Reply-To: <20050725192853.GA30567@2ka.mipt.ru>
Evgeniy Polyakov wrote:
> On Mon, Jul 25, 2005 at 04:32:32PM +0200, Patrick McHardy (kaber@trash.net) wrote:
>
>>If I understand correctly it tries to workaround some netlink
>>limitations (limited number of netlink families and multicast groups)
>>by sending everything to userspace and demultiplexing it there.
>>Same in the other direction, an additional layer on top of netlink
>>does basically the same thing netlink already does. This looks like
>>a step in the wrong direction to me, netlink should instead be fixed
>>to support what is needed.
>
> Not only it.
> The main _first_ idea was to simplify userspace mesasge handling as much
> as possible.
> In first releases I called it ioctl-ng - any module that want ot
> communicate with userspace in the way ioctl does,
Usually netlink is easily extendable by using nested TLVs. By hiding
this you basically remove this extensibility.
> requires skb allocation/freeing/handling.
> Does RTC driver writer need to know what is the difference between
> shared and cloned skb? Should kernel user of such message bus
> have to know about skb at all?
Netlink users don't have to care about shared or cloned skbs. I don't
think its a big issue to use alloc_skb and then the usual netlink
macros. Thomas added a number of macros that simplfiy use a lot.
But my main objection is that it sends everything to userspace even
if noone is listening. This can't be used for things that generate
lots of events, and also will get problematic is the number of users
increases.
> With char device I only need to register my callback - with kernel
> connector it is the same, but allows to use the whole power of netlink,
> especially without nice ioctl features like different pointer size
> in userspace and kernelspace.
You still have to take care of mixed 64/32 bit environments, u64 fields
for example are differently alligned.
> And number of free netlink sockets is _very_ small, especially
> if allocate new one for simple notifications, which can be easily done
> using connector.
Then fix it so we can use more families and groups. I started some work
on this, but I'm not sure if I have time to complete it.
> And netlink can be extended to support it - netlink is a transport
> protocol, it should not care about higher layer message handling,
> connector instead will deliver message to the end user in a very
> convenient form.
You can still built this stuff on top, but the workarounds for netlink
limitations need to be fixed in netlink.
> P.S. I've removed netdev@redhat.com - please do not add subscribers-only
> private mail lists.
Wasn't me :)
^ permalink raw reply
* Re: [RFC PATCH 1/4] PHY Abstraction Layer III (now with more splitiness)
From: Francois Romieu @ 2005-07-25 21:06 UTC (permalink / raw)
To: Andy Fleming; +Cc: Netdev, linuxppc-embedded
In-Reply-To: <Pine.LNX.4.61.0507251134470.15537@cde-tx32-ldt113.sps.mot.com>
Andy Fleming <afleming@freescale.com> :
> This patch contains the PHY layer itself, no phy drivers
[...]
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/Kconfig
[...]
> +config MARVELL_PHY
> + bool "Drivers for Marvell PHYs"
> + depends on PHYLIB
> + ---help---
> + Currently has a driver for the 88E1011S
> +
> +config DAVICOM_PHY
> + bool "Drivers for Davicom PHYs"
> + depends on PHYLIB
> + ---help---
> + Currently supports dm9161e and dm9131
[snip]
They try to escape...
[...]
> diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/Makefile
> @@ -0,0 +1,9 @@
> +# Makefile for Linux PHY drivers
> +
> +obj-$(CONFIG_PHYLIB) += phy.o phy_device.o mdio_bus.o
> +
> +obj-$(CONFIG_MARVELL_PHY) += marvell.o
> +obj-$(CONFIG_DAVICOM_PHY) += davicom.o
> +obj-$(CONFIG_CICADA_PHY) += cicada.o
> +obj-$(CONFIG_LXT_PHY) += lxt.o
> +obj-$(CONFIG_QSEMI_PHY) += qsemi.o
...and they are not alone. :o)
> diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/mdio_bus.c
> @@ -0,0 +1,175 @@
[...]
> +int mdiobus_register(struct mii_bus *bus)
> +{
> + int i;
> + int err = 0;
> +
> + spin_lock_init(&bus->mdio_lock);
> +
> + if (NULL == bus || NULL == bus->name ||
> + NULL == bus->read ||
> + NULL == bus->write)
Be spartan:
if (!bus || !bus->name || !bus->read || !bus->write)
> + return -EINVAL;
> +
> + if (bus->reset)
> + bus->reset(bus);
> +
> + for (i=0; i < PHY_MAX_ADDR; i++) {
for (i = 0; ...
> + struct phy_device *phydev;
> +
> + phydev = get_phy_device(bus, i);
> +
> + /* There's a PHY at this address
> + * We need to set:
> + * 1) IRQ
> + * 2) bus_id
> + * 3) parent
> + * 4) bus
> + * 5) mii_bus
> + * And, we need to register it */
> + if (phydev) {
> + phydev->irq = bus->irq[i];
> +
> + phydev->dev.parent = bus->dev;
> +
> + phydev->dev.bus = &mdio_bus_type;
> +
> + phydev->bus = bus;
> +
> + sprintf(phydev->dev.bus_id, "phy%d:%d", bus->id,
Imho you are going a bit too far with empty lines. Not a real issue.
> i);
Something decided to wrap the lines after you did the patch. It appears
several times in the patch (+ extra tabs/spaces at the end of the lines:
there are plenty of nice colorised editors around).
> +
> + err = device_register(&phydev->dev);
> +
> + if (err)
> + printk("phy %d did not register (%d)\n",
> + i, err);
Missing KERN_SOMETHING in the printk.
> +
> + /* If get_phy_device returned NULL, it may be
> + * because an error occurred. If so, we return
> + * that error */
> + } else if (errno)
> + return errno;
I'd rather use ERR_PTR/PTR_ERR/IS_ERR + goto in the first place
(then the previous printk will fit on a single line).
> +
> + bus->phy_map[i] = phydev;
> + }
> +
> + pr_info("%s: probed\n", bus->name);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(mdiobus_register);
> +
> +void mdiobus_unregister(struct mii_bus *bus)
> +{
> + int i;
> +
> + for (i=0; i < PHY_MAX_ADDR; i++)
for( i = 0, ... + missing brace.
[...]
> +static int mdio_bus_suspend(struct device * dev, u32 state)
> +{
> + int ret = 0;
> +
> + if (dev->driver && dev->driver->suspend) {
> + ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
> + if (ret == 0)
> + ret = dev->driver->suspend(dev, state,
> SUSPEND_SAVE_STATE);
> + if (ret == 0)
> + ret = dev->driver->suspend(dev, state,
> SUSPEND_POWER_DOWN);
> + }
Copy/paste abuse:
struct device_driver *drv = dev->driver;
(appears in several functions)
[...]
> +struct bus_type mdio_bus_type = {
> + .name = "mdio_bus",
> + .match = mdio_bus_match,
> + .suspend= mdio_bus_suspend,
^^
[...]
> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/net/phy/phy.c
[...]
> +int phy_read(struct phy_device *phydev, u16 regnum);
> +int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
> +void phy_change(void *data);
> +void phy_timer(unsigned long data);
phy_read and phy_write do not need to be forward-declared.
Not sure if it is possible for phy_{change/timer} as well.
> +
> +/* Convenience function to print out the current phy status
> + */
> +void phy_print_status(struct phy_device *phydev)
> +{
> + pr_info("%s: Link is %s", phydev->dev.bus_id,
> + phydev->link ? "Up" : "Down");
> + if (phydev->link)
> + printk(" - %d/%s", phydev->speed,
Missing KERN_SOMETHING in the printk.
[...]
> +static inline int phy_aneg_done(struct phy_device *phydev)
> +{
> + int retval;
> +
> + retval = phy_read(phydev, MII_BMSR);
> +
> + if (retval < 0)
> + return retval;
> +
> + return retval & BMSR_ANEGCOMPLETE;
Please use a ternary operator.
> +}
> +
> +/* phy_start_aneg
> + *
> + * description: Calls the PHY driver's config_aneg, and then
> + * sets the PHY state to PHY_AN if auto-negotiation is enabled,
> + * and to PHY_FORCING if auto-negotiation is disabled. Unless
> + * the PHY is currently HALTED.
> + */
> +int phy_start_aneg(struct phy_device *phydev)
> +{
> + int err = 0;
Unneeded initialization.
> +
> + spin_lock(&phydev->lock);
> +
> + if (AUTONEG_DISABLE == phydev->autoneg)
> + phy_sanitize_settings(phydev);
> +
> + err = phydev->drv->config_aneg(phydev);
> +
> + if (err < 0)
> + return err;
The lock should be released. Add a 'goto out_unlock;' ?
> +
> + if (phydev->state != PHY_HALTED) {
> + if (AUTONEG_ENABLE == phydev->autoneg) {
> + phydev->state = PHY_AN;
> + phydev->link_timeout = PHY_AN_TIMEOUT;
> + } else {
> + phydev->state = PHY_FORCING;
> + phydev->link_timeout = PHY_FORCE_TIMEOUT;
> + }
> + }
> +
out_unlock:
> + spin_unlock(&phydev->lock);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(phy_start_aneg);
> +
> +
[...]
> +/* A mapping of all SUPPORTED settings to speed/duplex */
> +static struct phy_setting settings[] = {
> + { .speed = 10000, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_10000baseT_Full,
> + },
> + { .speed = SPEED_1000, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_1000baseT_Full,
> + },
> + { .speed = SPEED_1000, .duplex = DUPLEX_HALF,
> + .setting = SUPPORTED_1000baseT_Half,
> + },
> + { .speed = SPEED_100, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_100baseT_Full,
> + },
> + { .speed = SPEED_100, .duplex = DUPLEX_HALF,
> + .setting = SUPPORTED_100baseT_Half,
> + },
> + { .speed = SPEED_10, .duplex = DUPLEX_FULL,
> + .setting = SUPPORTED_10baseT_Full,
> + },
> + { .speed = SPEED_10, .duplex = DUPLEX_HALF,
> + .setting = SUPPORTED_10baseT_Half,
> + },
> +};
Would you veto some macro to initialise this array ?
> +
> +#define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting))
The kernel provides ARRAY_SIZE
[...]
> +static inline int phy_find_setting(int speed, int duplex)
> +{
> + int idx = 0;
> +
> + while (idx < MAX_NUM_SETTINGS &&
> + (settings[idx].speed != speed ||
> + settings[idx].duplex != duplex))
> + idx++;
"for" loop in disguise ?
> +
> + return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
Ok (dunno if "idx % MAX_NUM_SETTINGS" is more idiomatic or not).
[...]
> +int phy_start_interrupts(struct phy_device *phydev)
> +{
> + int err = 0;
> +
> + INIT_WORK(&phydev->phy_queue, phy_change, phydev);
> +
> + if (request_irq(phydev->irq, phy_interrupt,
> + SA_SHIRQ,
> + "phy_interrupt",
> + phydev) < 0) {
Please, don't do that :o(
err = request_irq(phydev->irq, phy_interrupt, SA_SHIRQ,
"phy_interrupt", phydev);
if (err < 0)
...
> + printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
> + phydev->bus->name,
> + phydev->irq);
> + phydev->irq = PHY_POLL;
> + return 0;
The description of the function says "Returns 0 on success".
[...]
> +/* Bring down the PHY link, and stop checking the status. */
> +void phy_stop(struct phy_device *phydev)
> +{
> + spin_lock(&phydev->lock);
> +
> + if (PHY_HALTED == phydev->state) {
> + spin_unlock(&phydev->lock);
> + return;
> + }
"goto out_unlock;"
> +
> + if (phydev->irq != PHY_POLL) {
> + /* Clear any pending interrupts */
> + phy_clear_interrupt(phydev);
> +
> + /* Disable PHY Interrupts */
> + phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
> + }
> +
> + phydev->state = PHY_HALTED;
> +
> + spin_unlock(&phydev->lock);
> +}
[...]
> +struct phy_device * get_phy_device(struct mii_bus *bus, uint addr)
> +{
uint ?
> + int phy_reg;
> + u32 phy_id;
> + struct phy_device *dev = NULL;
> +
> + errno = 0;
A bit ugly.
> +
> + /* Grab the bits from PHYIR1, and put them
> + * in the upper half */
> + phy_reg = bus->read(bus, addr, MII_PHYSID1);
> +
> + if (phy_reg < 0) {
> + errno = phy_reg;
> + return NULL;
dev = ERR_PTR(phy_reg);
goto out;
...
> + }
> +
> + phy_id = (phy_reg & 0xffff) << 16;
> +
> + /* Grab the bits from PHYIR2, and put them in the lower half */
> + phy_reg = bus->read(bus, addr, MII_PHYSID2);
> +
> + if (phy_reg < 0) {
> + errno = phy_reg;
> + return NULL;
> + }
> +
> + phy_id |= (phy_reg & 0xffff);
> +
> + /* If the phy_id is all Fs, there is no device there */
> + if (0xffffffff == phy_id)
> + return NULL;
> +
> + /* Otherwise, we allocate the device, and initialize the
> + * default values */
> + dev = kmalloc(sizeof(*dev), GFP_KERNEL);
> +
> + if (NULL == dev) {
> + errno = -ENOMEM;
> + return NULL;
> + }
> +
> + memset(dev, 0, sizeof(*dev));
The kernel provides kcalloc.
[...]
> +static int phy_compare_id(struct device *dev, void *data)
> +{
> + const char *name = data;
> +
> + if (strcmp(name, dev->bus_id) == 0)
> + return 1;
> + return 0;
Ternary operator ?
> +}
> +
> +struct phy_device *phy_attach(struct net_device *dev,
> + const char *phy_id, u32 flags)
> +{
> + struct phy_device *phydev = NULL;
Useless initialization.
> + struct bus_type *bus = &mdio_bus_type;
> + struct device *d;
> +
> + /* Search the list of PHY devices on the mdio bus for the
> + * PHY with the requested name */
> + d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id);
> +
> + if (d) {
> + phydev = to_phy_device(d);
> + } else {
> + printk(KERN_ERR "%s not found\n", phy_id);
> + errno = -ENODEV;
> + return NULL;
> + }
> +
> + /* Assume that if there is no driver, that it doesn't
> + * exist, and we should use the genphy driver. */
> + if (NULL == phydev->dev.driver) {
> + int err;
> + down_write(&phydev->dev.bus->subsys.rwsem);
> + phydev->dev.driver = &genphy_driver.driver;
> +
> + err = phydev->dev.driver->probe(&phydev->dev);
Would it be possible to s/phydev->dev/d/ ?
--
Ueimor
^ permalink raw reply
* [RFC PATCH 3/4]: PHY Abstraction Layer III (now with more splitiness)
From: Andy Fleming @ 2005-07-25 19:56 UTC (permalink / raw)
To: Netdev, linuxppc-embedded
This patch contains the changes to the gianfar driver as an example of how
to change a driver to use the PHY Abstraction Layer. Note that the patch,
while large, contains mostly deletions. The "hard" work is all in
gianfar_mii.c, which implements an MDIO bus interface.
This is the 3rd and final patch (of four). The fourth patch contains
platform changes for the MPC85xx systems, and is probably not relevant to
the understanding of the first 3 patches. Certainly it is not relevant to
the netdev community, as it consists of board-specific structures and
code.
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2047,6 +2047,8 @@ config BNX2
config GIANFAR
tristate "Gianfar Ethernet"
depends on 85xx || 83xx
+ select PHYLIB
+ select PHYCONTROL
help
This driver supports the Gigabit TSEC on the MPC85xx
family of chips, and the FEC on the 8540
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
-gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_phy.o
+gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_mii.o
#
# link order important here
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -29,12 +29,7 @@
* define the configuration needed by the board are defined in a
* board structure in arch/ppc/platforms (though I do not
* discount the possibility that other architectures could one
- * day be supported. One assumption the driver currently makes
- * is that the PHY is configured in such a way to advertise all
- * capabilities. This is a sensible default, and on certain
- * PHYs, changing this default encounters substantial errata
- * issues. Future versions may remove this requirement, but for
- * now, it is best for the firmware to ensure this is the case.
+ * day be supported.
*
* The Gianfar Ethernet Controller uses a ring of buffer
* descriptors. The beginning is indicated by a register
@@ -47,7 +42,7 @@
* corresponding bit in the IMASK register is also set (if
* interrupt coalescing is active, then the interrupt may not
* happen immediately, but will wait until either a set number
- * of frames or amount of time have passed.). In NAPI, the
+ * of frames or amount of time have passed). In NAPI, the
* interrupt handler will signal there is work to be done, and
* exit. Without NAPI, the packet(s) will be handled
* immediately. Both methods will start at the last known empty
@@ -75,6 +70,7 @@
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
+#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -97,9 +93,11 @@
#include <linux/version.h>
#include <linux/dma-mapping.h>
#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
#include "gianfar.h"
-#include "gianfar_phy.h"
+#include "gianfar_mii.h"
#define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000
@@ -113,9 +111,8 @@
#endif
const char gfar_driver_name[] = "Gianfar Ethernet";
-const char gfar_driver_version[] = "1.1";
+const char gfar_driver_version[] = "1.2";
-int startup_gfar(struct net_device *dev);
static int gfar_enet_open(struct net_device *dev);
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void gfar_timeout(struct net_device *dev);
@@ -126,17 +123,13 @@ static int gfar_set_mac_address(struct n
static int gfar_change_mtu(struct net_device *dev, int new_mtu);
static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs);
static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs);
-static irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void gfar_phy_change(void *data);
-static void gfar_phy_timer(unsigned long data);
static void adjust_link(struct net_device *dev);
static void init_registers(struct net_device *dev);
static int init_phy(struct net_device *dev);
static int gfar_probe(struct device *device);
static int gfar_remove(struct device *device);
-void free_skb_resources(struct gfar_private *priv);
+static void free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
#ifdef CONFIG_GFAR_NAPI
@@ -144,7 +137,6 @@ static int gfar_poll(struct net_device *
#endif
int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
-static void gfar_phy_startup_timer(unsigned long data);
static void gfar_vlan_rx_register(struct net_device *netdev,
struct vlan_group *grp);
static void gfar_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
@@ -162,6 +154,9 @@ int gfar_uses_fcb(struct gfar_private *p
else
return 0;
}
+
+/* Set up the ethernet device structure, private data,
+ * and anything else we need before we start */
static int gfar_probe(struct device *device)
{
u32 tempval;
@@ -175,7 +170,7 @@ static int gfar_probe(struct device *dev
einfo = (struct gianfar_platform_data *) pdev->dev.platform_data;
- if (einfo == NULL) {
+ if (NULL == einfo) {
printk(KERN_ERR "gfar %d: Missing additional data!\n",
pdev->id);
@@ -185,7 +180,7 @@ static int gfar_probe(struct device *dev
/* Create an ethernet device instance */
dev = alloc_etherdev(sizeof (*priv));
- if (dev == NULL)
+ if (NULL == dev)
return -ENOMEM;
priv = netdev_priv(dev);
@@ -207,20 +202,11 @@ static int gfar_probe(struct device *dev
priv->regs = (struct gfar *)
ioremap(r->start, sizeof (struct gfar));
- if (priv->regs == NULL) {
+ if (NULL == priv->regs) {
err = -ENOMEM;
goto regs_fail;
}
- /* Set the PHY base address */
- priv->phyregs = (struct gfar *)
- ioremap(einfo->phy_reg_addr, sizeof (struct gfar));
-
- if (priv->phyregs == NULL) {
- err = -ENOMEM;
- goto phy_regs_fail;
- }
-
spin_lock_init(&priv->lock);
dev_set_drvdata(device, dev);
@@ -386,12 +372,10 @@ static int gfar_probe(struct device *dev
return 0;
register_fail:
- iounmap((void *) priv->phyregs);
-phy_regs_fail:
iounmap((void *) priv->regs);
regs_fail:
free_netdev(dev);
- return -ENOMEM;
+ return err;
}
static int gfar_remove(struct device *device)
@@ -402,108 +386,41 @@ static int gfar_remove(struct device *de
dev_set_drvdata(device, NULL);
iounmap((void *) priv->regs);
- iounmap((void *) priv->phyregs);
free_netdev(dev);
return 0;
}
-/* Configure the PHY for dev.
- * returns 0 if success. -1 if failure
+/* Initializes driver's PHY state, and attaches to the PHY.
+ * Returns 0 on success, errno on failure to attach.
*/
static int init_phy(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct phy_info *curphy;
- unsigned int timeout = PHY_INIT_TIMEOUT;
- struct gfar *phyregs = priv->phyregs;
- struct gfar_mii_info *mii_info;
- int err;
+ uint gigabit_support =
+ priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
+ SUPPORTED_1000baseT_Full : 0;
+ struct phy_device *phydev;
priv->oldlink = 0;
priv->oldspeed = 0;
priv->oldduplex = -1;
- mii_info = kmalloc(sizeof(struct gfar_mii_info),
- GFP_KERNEL);
-
- if(NULL == mii_info) {
- if (netif_msg_ifup(priv))
- printk(KERN_ERR "%s: Could not allocate mii_info\n",
- dev->name);
- return -ENOMEM;
- }
-
- mii_info->speed = SPEED_1000;
- mii_info->duplex = DUPLEX_FULL;
- mii_info->pause = 0;
- mii_info->link = 1;
-
- mii_info->advertising = (ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full |
- ADVERTISED_1000baseT_Full);
- mii_info->autoneg = 1;
+ phydev = phy_connect(dev, priv->einfo->bus_id, &adjust_link, 0);
- spin_lock_init(&mii_info->mdio_lock);
-
- mii_info->mii_id = priv->einfo->phyid;
-
- mii_info->dev = dev;
-
- mii_info->mdio_read = &read_phy_reg;
- mii_info->mdio_write = &write_phy_reg;
-
- priv->mii_info = mii_info;
-
- /* Reset the management interface */
- gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
-
- /* Setup the MII Mgmt clock speed */
- gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
-
- /* Wait until the bus is free */
- while ((gfar_read(&phyregs->miimind) & MIIMIND_BUSY) &&
- timeout--)
- cpu_relax();
-
- if(timeout <= 0) {
- printk(KERN_ERR "%s: The MII Bus is stuck!\n",
- dev->name);
- err = -1;
- goto bus_fail;
- }
-
- /* get info for this PHY */
- curphy = get_phy_info(priv->mii_info);
-
- if (curphy == NULL) {
- if (netif_msg_ifup(priv))
- printk(KERN_ERR "%s: No PHY found\n", dev->name);
- err = -1;
- goto no_phy;
+ if(NULL == phydev) {
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+ return errno;
}
- mii_info->phyinfo = curphy;
+ /* Remove any features not supported by the controller */
+ phydev->supported &= (GFAR_SUPPORTED | gigabit_support);
+ phydev->advertising = phydev->supported;
- /* Run the commands which initialize the PHY */
- if(curphy->init) {
- err = curphy->init(priv->mii_info);
-
- if (err)
- goto phy_init_fail;
- }
+ priv->phydev = phydev;
return 0;
-
-phy_init_fail:
-no_phy:
-bus_fail:
- kfree(mii_info);
-
- return err;
}
static void init_registers(struct net_device *dev)
@@ -603,24 +520,13 @@ void stop_gfar(struct net_device *dev)
struct gfar *regs = priv->regs;
unsigned long flags;
+ phy_stop(priv->phydev);
+
/* Lock it down */
spin_lock_irqsave(&priv->lock, flags);
- /* Tell the kernel the link is down */
- priv->mii_info->link = 0;
- adjust_link(dev);
-
gfar_halt(dev);
- if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
- /* Clear any pending interrupts */
- mii_clear_phy_interrupt(priv->mii_info);
-
- /* Disable PHY Interrupts */
- mii_configure_phy_interrupt(priv->mii_info,
- MII_INTERRUPT_DISABLED);
- }
-
spin_unlock_irqrestore(&priv->lock, flags);
/* Free the IRQs */
@@ -629,13 +535,7 @@ void stop_gfar(struct net_device *dev)
free_irq(priv->interruptTransmit, dev);
free_irq(priv->interruptReceive, dev);
} else {
- free_irq(priv->interruptTransmit, dev);
- }
-
- if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
- free_irq(priv->einfo->interruptPHY, dev);
- } else {
- del_timer_sync(&priv->phy_info_timer);
+ free_irq(priv->interruptTransmit, dev);
}
free_skb_resources(priv);
@@ -649,7 +549,7 @@ void stop_gfar(struct net_device *dev)
/* If there are any tx skbs or rx skbs still around, free them.
* Then free tx_skbuff and rx_skbuff */
-void free_skb_resources(struct gfar_private *priv)
+static void free_skb_resources(struct gfar_private *priv)
{
struct rxbd8 *rxbdp;
struct txbd8 *txbdp;
@@ -770,7 +670,7 @@ int startup_gfar(struct net_device *dev)
(struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
priv->tx_ring_size, GFP_KERNEL);
- if (priv->tx_skbuff == NULL) {
+ if (NULL == priv->tx_skbuff) {
if (netif_msg_ifup(priv))
printk(KERN_ERR "%s: Could not allocate tx_skbuff\n",
dev->name);
@@ -785,7 +685,7 @@ int startup_gfar(struct net_device *dev)
(struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
priv->rx_ring_size, GFP_KERNEL);
- if (priv->rx_skbuff == NULL) {
+ if (NULL == priv->rx_skbuff) {
if (netif_msg_ifup(priv))
printk(KERN_ERR "%s: Could not allocate rx_skbuff\n",
dev->name);
@@ -879,13 +779,7 @@ int startup_gfar(struct net_device *dev)
}
}
- /* Set up the PHY change work queue */
- INIT_WORK(&priv->tq, gfar_phy_change, dev);
-
- init_timer(&priv->phy_info_timer);
- priv->phy_info_timer.function = &gfar_phy_startup_timer;
- priv->phy_info_timer.data = (unsigned long) priv->mii_info;
- mod_timer(&priv->phy_info_timer, jiffies + HZ);
+ phy_start(priv->phydev);
/* Configure the coalescing support */
if (priv->txcoalescing)
@@ -933,11 +827,6 @@ tx_skb_fail:
priv->tx_bd_base,
gfar_read(®s->tbase0));
- if (priv->mii_info->phyinfo->close)
- priv->mii_info->phyinfo->close(priv->mii_info);
-
- kfree(priv->mii_info);
-
return err;
}
@@ -1035,7 +924,7 @@ static int gfar_start_xmit(struct sk_buf
txbdp->status &= TXBD_WRAP;
/* Set up checksumming */
- if ((dev->features & NETIF_F_IP_CSUM)
+ if ((dev->features & NETIF_F_IP_CSUM)
&& (CHECKSUM_HW == skb->ip_summed)) {
fcb = gfar_add_fcb(skb, txbdp);
gfar_tx_checksum(skb, fcb);
@@ -1103,11 +992,9 @@ static int gfar_close(struct net_device
struct gfar_private *priv = netdev_priv(dev);
stop_gfar(dev);
- /* Shutdown the PHY */
- if (priv->mii_info->phyinfo->close)
- priv->mii_info->phyinfo->close(priv->mii_info);
-
- kfree(priv->mii_info);
+ /* Disconnect from the PHY */
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
netif_stop_queue(dev);
@@ -1343,7 +1230,7 @@ struct sk_buff * gfar_new_skb(struct net
while ((!skb) && timeout--)
skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT);
- if (skb == NULL)
+ if (NULL == skb)
return NULL;
/* We need the data buffer to be aligned properly. We will reserve
@@ -1490,7 +1377,7 @@ static int gfar_process_frame(struct net
struct gfar_private *priv = netdev_priv(dev);
struct rxfcb *fcb = NULL;
- if (skb == NULL) {
+ if (NULL == skb) {
if (netif_msg_rx_err(priv))
printk(KERN_WARNING "%s: Missing skb!!.\n", dev->name);
priv->stats.rx_dropped++;
@@ -1718,131 +1605,9 @@ static irqreturn_t gfar_interrupt(int ir
return IRQ_HANDLED;
}
-static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *) dev_id;
- struct gfar_private *priv = netdev_priv(dev);
-
- /* Clear the interrupt */
- mii_clear_phy_interrupt(priv->mii_info);
-
- /* Disable PHY interrupts */
- mii_configure_phy_interrupt(priv->mii_info,
- MII_INTERRUPT_DISABLED);
-
- /* Schedule the phy change */
- schedule_work(&priv->tq);
-
- return IRQ_HANDLED;
-}
-
-/* Scheduled by the phy_interrupt/timer to handle PHY changes */
-static void gfar_phy_change(void *data)
-{
- struct net_device *dev = (struct net_device *) data;
- struct gfar_private *priv = netdev_priv(dev);
- int result = 0;
-
- /* Delay to give the PHY a chance to change the
- * register state */
- msleep(1);
-
- /* Update the link, speed, duplex */
- result = priv->mii_info->phyinfo->read_status(priv->mii_info);
-
- /* Adjust the known status as long as the link
- * isn't still coming up */
- if((0 == result) || (priv->mii_info->link == 0))
- adjust_link(dev);
-
- /* Reenable interrupts, if needed */
- if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR)
- mii_configure_phy_interrupt(priv->mii_info,
- MII_INTERRUPT_ENABLED);
-}
-
-/* Called every so often on systems that don't interrupt
- * the core for PHY changes */
-static void gfar_phy_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *) data;
- struct gfar_private *priv = netdev_priv(dev);
-
- schedule_work(&priv->tq);
-
- mod_timer(&priv->phy_info_timer, jiffies +
- GFAR_PHY_CHANGE_TIME * HZ);
-}
-
-/* Keep trying aneg for some time
- * If, after GFAR_AN_TIMEOUT seconds, it has not
- * finished, we switch to forced.
- * Either way, once the process has completed, we either
- * request the interrupt, or switch the timer over to
- * using gfar_phy_timer to check status */
-static void gfar_phy_startup_timer(unsigned long data)
-{
- int result;
- static int secondary = GFAR_AN_TIMEOUT;
- struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
- struct gfar_private *priv = netdev_priv(mii_info->dev);
-
- /* Configure the Auto-negotiation */
- result = mii_info->phyinfo->config_aneg(mii_info);
-
- /* If autonegotiation failed to start, and
- * we haven't timed out, reset the timer, and return */
- if (result && secondary--) {
- mod_timer(&priv->phy_info_timer, jiffies + HZ);
- return;
- } else if (result) {
- /* Couldn't start autonegotiation.
- * Try switching to forced */
- mii_info->autoneg = 0;
- result = mii_info->phyinfo->config_aneg(mii_info);
-
- /* Forcing failed! Give up */
- if(result) {
- if (netif_msg_link(priv))
- printk(KERN_ERR "%s: Forcing failed!\n",
- mii_info->dev->name);
- return;
- }
- }
-
- /* Kill the timer so it can be restarted */
- del_timer_sync(&priv->phy_info_timer);
-
- /* Grab the PHY interrupt, if necessary/possible */
- if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
- if (request_irq(priv->einfo->interruptPHY,
- phy_interrupt,
- SA_SHIRQ,
- "phy_interrupt",
- mii_info->dev) < 0) {
- if (netif_msg_intr(priv))
- printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
- mii_info->dev->name,
- priv->einfo->interruptPHY);
- } else {
- mii_configure_phy_interrupt(priv->mii_info,
- MII_INTERRUPT_ENABLED);
- return;
- }
- }
-
- /* Start the timer again, this time in order to
- * handle a change in status */
- init_timer(&priv->phy_info_timer);
- priv->phy_info_timer.function = &gfar_phy_timer;
- priv->phy_info_timer.data = (unsigned long) mii_info->dev;
- mod_timer(&priv->phy_info_timer, jiffies +
- GFAR_PHY_CHANGE_TIME * HZ);
-}
-
/* Called every time the controller might need to be made
* aware of new link state. The PHY code conveys this
- * information through variables in the priv structure, and this
+ * information through variables in the phydev structure, and this
* function converts those variables into the appropriate
* register values, and can bring down the device if needed.
*/
@@ -1850,84 +1615,68 @@ static void adjust_link(struct net_devic
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
- u32 tempval;
- struct gfar_mii_info *mii_info = priv->mii_info;
+ unsigned long flags;
+ struct phy_device *phydev = priv->phydev;
+ int new_state = 0;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (phydev->link) {
+ u32 tempval = gfar_read(®s->maccfg2);
- if (mii_info->link) {
/* Now we make sure that we can be in full duplex mode.
* If not, we operate in half-duplex mode. */
- if (mii_info->duplex != priv->oldduplex) {
- if (!(mii_info->duplex)) {
- tempval = gfar_read(®s->maccfg2);
+ if (phydev->duplex != priv->oldduplex) {
+ new_state = 1;
+ if (!(phydev->duplex))
tempval &= ~(MACCFG2_FULL_DUPLEX);
- gfar_write(®s->maccfg2, tempval);
-
- if (netif_msg_link(priv))
- printk(KERN_INFO "%s: Half Duplex\n",
- dev->name);
- } else {
- tempval = gfar_read(®s->maccfg2);
+ else
tempval |= MACCFG2_FULL_DUPLEX;
- gfar_write(®s->maccfg2, tempval);
- if (netif_msg_link(priv))
- printk(KERN_INFO "%s: Full Duplex\n",
- dev->name);
- }
-
- priv->oldduplex = mii_info->duplex;
+ priv->oldduplex = phydev->duplex;
}
- if (mii_info->speed != priv->oldspeed) {
- switch (mii_info->speed) {
+ if (phydev->speed != priv->oldspeed) {
+ new_state = 1;
+ switch (phydev->speed) {
case 1000:
- tempval = gfar_read(®s->maccfg2);
tempval =
((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
- gfar_write(®s->maccfg2, tempval);
break;
case 100:
case 10:
- tempval = gfar_read(®s->maccfg2);
tempval =
((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
- gfar_write(®s->maccfg2, tempval);
break;
default:
if (netif_msg_link(priv))
printk(KERN_WARNING
- "%s: Ack! Speed (%d) is not 10/100/1000!\n",
- dev->name, mii_info->speed);
+ "%s: Ack! Speed (%d) is not 10/100/1000!\n",
+ dev->name, phydev->speed);
break;
}
- if (netif_msg_link(priv))
- printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
- mii_info->speed);
-
- priv->oldspeed = mii_info->speed;
+ priv->oldspeed = phydev->speed;
}
+ gfar_write(®s->maccfg2, tempval);
+
if (!priv->oldlink) {
- if (netif_msg_link(priv))
- printk(KERN_INFO "%s: Link is up\n", dev->name);
+ new_state = 1;
priv->oldlink = 1;
- netif_carrier_on(dev);
netif_schedule(dev);
}
- } else {
- if (priv->oldlink) {
- if (netif_msg_link(priv))
- printk(KERN_INFO "%s: Link is down\n",
- dev->name);
- priv->oldlink = 0;
- priv->oldspeed = 0;
- priv->oldduplex = -1;
- netif_carrier_off(dev);
- }
+ } else if (priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
}
-}
+ if (new_state && netif_msg_link(priv))
+ phy_print_status(phydev);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
/* Update the hash table based on the current list of multicast
* addresses we subscribe to. Also, change the promiscuity of
@@ -2122,12 +1871,23 @@ static struct device_driver gfar_driver
static int __init gfar_init(void)
{
- return driver_register(&gfar_driver);
+ int err = gfar_mdio_init();
+
+ if (err)
+ return err;
+
+ err = driver_register(&gfar_driver);
+
+ if (err)
+ gfar_mdio_exit();
+
+ return err;
}
static void __exit gfar_exit(void)
{
driver_unregister(&gfar_driver);
+ gfar_mdio_exit();
}
module_init(gfar_init);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -17,7 +17,6 @@
*
* Still left to do:
* -Add support for module parameters
- * -Add support for ethtool -s
* -Add patch for ethtool phys id
*/
#ifndef __GIANFAR_H
@@ -37,7 +36,8 @@
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
-#include <linux/fsl_devices.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -48,7 +48,8 @@
#include <linux/workqueue.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
-#include "gianfar_phy.h"
+#include <linux/fsl_devices.h>
+#include "gianfar_mii.h"
/* The maximum number of packets to be handled in one call of gfar_poll */
#define GFAR_DEV_WEIGHT 64
@@ -73,7 +74,7 @@
#define PHY_INIT_TIMEOUT 100000
#define GFAR_PHY_CHANGE_TIME 2
-#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, "
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.2, "
#define DRV_NAME "gfar-enet"
extern const char gfar_driver_name[];
extern const char gfar_driver_version[];
@@ -578,12 +579,7 @@ struct gfar {
u32 hafdup; /* 0x.50c - Half Duplex Register */
u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */
u8 res18[12];
- u32 miimcfg; /* 0x.520 - MII Management Configuration Register */
- u32 miimcom; /* 0x.524 - MII Management Command Register */
- u32 miimadd; /* 0x.528 - MII Management Address Register */
- u32 miimcon; /* 0x.52c - MII Management Control Register */
- u32 miimstat; /* 0x.530 - MII Management Status Register */
- u32 miimind; /* 0x.534 - MII Management Indicator Register */
+ u8 gfar_mii_regs[24]; /* See gianfar_phy.h */
u8 res19[4];
u32 ifstat; /* 0x.53c - Interface Status Register */
u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */
@@ -688,9 +684,6 @@ struct gfar_private {
struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
u32 *hash_regs[16];
int hash_width;
- struct gfar *phyregs;
- struct work_struct tq;
- struct timer_list phy_info_timer;
struct net_device_stats stats; /* linux network statistics */
struct gfar_extra_stats extra_stats;
spinlock_t lock;
@@ -710,7 +703,8 @@ struct gfar_private {
unsigned int interruptError;
struct gianfar_platform_data *einfo;
- struct gfar_mii_info *mii_info;
+ struct phy_device *phydev;
+ struct mii_bus *mii_bus;
int oldspeed;
int oldduplex;
int oldlink;
@@ -731,5 +725,13 @@ extern inline void gfar_write(volatile u
}
extern struct ethtool_ops *gfar_op_array[];
+
+extern irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
+extern int startup_gfar(struct net_device *dev);
+extern void stop_gfar(struct net_device *dev);
+extern void gfar_halt(struct net_device *dev);
+extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
+ int enable, u32 regnum, u32 read);
+void gfar_setup_stashing(struct net_device *dev);
#endif /* __GIANFAR_H */
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -39,17 +39,18 @@
#include <asm/types.h>
#include <asm/uaccess.h>
#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
#include "gianfar.h"
#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
-extern int startup_gfar(struct net_device *dev);
-extern void stop_gfar(struct net_device *dev);
-extern void gfar_halt(struct net_device *dev);
extern void gfar_start(struct net_device *dev);
extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
+#define GFAR_MAX_COAL_USECS 0xffff
+#define GFAR_MAX_COAL_FRAMES 0xff
static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
u64 * buf);
static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
@@ -182,38 +183,32 @@ static void gfar_gdrvinfo(struct net_dev
drvinfo->eedump_len = 0;
}
+
+static int gfar_ssettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+
+ if (NULL == phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
+
/* Return the current settings in the ethtool_cmd structure */
static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct gfar_private *priv = netdev_priv(dev);
- uint gigabit_support =
- priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
- SUPPORTED_1000baseT_Full : 0;
- uint gigabit_advert =
- priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
- ADVERTISED_1000baseT_Full: 0;
-
- cmd->supported = (SUPPORTED_10baseT_Half
- | SUPPORTED_100baseT_Half
- | SUPPORTED_100baseT_Full
- | gigabit_support | SUPPORTED_Autoneg);
-
- /* For now, we always advertise everything */
- cmd->advertising = (ADVERTISED_10baseT_Half
- | ADVERTISED_100baseT_Half
- | ADVERTISED_100baseT_Full
- | gigabit_advert | ADVERTISED_Autoneg);
-
- cmd->speed = priv->mii_info->speed;
- cmd->duplex = priv->mii_info->duplex;
- cmd->port = PORT_MII;
- cmd->phy_address = priv->mii_info->mii_id;
- cmd->transceiver = XCVR_EXTERNAL;
- cmd->autoneg = AUTONEG_ENABLE;
+ struct phy_device *phydev = priv->phydev;
+
+ if (NULL == phydev)
+ return -ENODEV;
+
cmd->maxtxpkt = priv->txcount;
cmd->maxrxpkt = priv->rxcount;
- return 0;
+ return phy_ethtool_gset(phydev, cmd);
}
/* Return the length of the register structure */
@@ -241,14 +236,14 @@ static unsigned int gfar_usecs2ticks(str
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->mii_info->speed) {
- case 1000:
+ switch (priv->phydev->speed) {
+ case SPEED_1000:
count = GFAR_GBIT_TIME;
break;
- case 100:
+ case SPEED_100:
count = GFAR_100_TIME;
break;
- case 10:
+ case SPEED_10:
default:
count = GFAR_10_TIME;
break;
@@ -265,14 +260,14 @@ static unsigned int gfar_ticks2usecs(str
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->mii_info->speed) {
- case 1000:
+ switch (priv->phydev->speed) {
+ case SPEED_1000:
count = GFAR_GBIT_TIME;
break;
- case 100:
+ case SPEED_100:
count = GFAR_100_TIME;
break;
- case 10:
+ case SPEED_10:
default:
count = GFAR_10_TIME;
break;
@@ -292,6 +287,9 @@ static int gfar_gcoalesce(struct net_dev
if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
+ if (NULL == priv->phydev)
+ return -ENODEV;
+
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
cvals->rx_max_coalesced_frames = priv->rxcount;
@@ -348,6 +346,22 @@ static int gfar_scoalesce(struct net_dev
else
priv->rxcoalescing = 1;
+ if (NULL == priv->phydev)
+ return -ENODEV;
+
+ /* Check the bounds of the values */
+ if (cvals->rx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
+ pr_info("Coalescing is limited to %d microseconds\n",
+ GFAR_MAX_COAL_USECS);
+ return -EINVAL;
+ }
+
+ if (cvals->rx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
+ pr_info("Coalescing is limited to %d frames\n",
+ GFAR_MAX_COAL_FRAMES);
+ return -EINVAL;
+ }
+
priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs);
priv->rxcount = cvals->rx_max_coalesced_frames;
@@ -358,6 +372,19 @@ static int gfar_scoalesce(struct net_dev
else
priv->txcoalescing = 1;
+ /* Check the bounds of the values */
+ if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
+ pr_info("Coalescing is limited to %d microseconds\n",
+ GFAR_MAX_COAL_USECS);
+ return -EINVAL;
+ }
+
+ if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
+ pr_info("Coalescing is limited to %d frames\n",
+ GFAR_MAX_COAL_FRAMES);
+ return -EINVAL;
+ }
+
priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs);
priv->txcount = cvals->tx_max_coalesced_frames;
@@ -536,6 +563,7 @@ static void gfar_set_msglevel(struct net
struct ethtool_ops gfar_ethtool_ops = {
.get_settings = gfar_gsettings,
+ .set_settings = gfar_ssettings,
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/gianfar_mii.c
@@ -0,0 +1,220 @@
+/*
+ * drivers/net/gianfar_mii.c
+ *
+ * Gianfar Ethernet Driver -- MIIM bus implementation
+ * Provides Bus interface for MIIM regs
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <asm/ocp.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "gianfar.h"
+#include "gianfar_mii.h"
+
+/* Write value to the PHY at mii_id at register regnum,
+ * on the bus, waiting until the write is done before returning.
+ * All PHY configuration is done through the TSEC1 MIIM regs */
+int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
+{
+ struct gfar_mii *regs = bus->priv;
+
+ /* Set the PHY address and the register address we want to write */
+ gfar_write(®s->miimadd, (mii_id << 8) | regnum);
+
+ /* Write out the value we want */
+ gfar_write(®s->miimcon, value);
+
+ /* Wait for the transaction to finish */
+ while (gfar_read(®s->miimind) & MIIMIND_BUSY)
+ cpu_relax();
+
+ return 0;
+}
+
+/* Read the bus for PHY at addr mii_id, register regnum, and
+ * return the value. Clears miimcom first. All PHY
+ * configuration has to be done through the TSEC1 MIIM regs */
+int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct gfar_mii *regs = bus->priv;
+ u16 value;
+
+ /* Set the PHY address and the register address we want to read */
+ gfar_write(®s->miimadd, (mii_id << 8) | regnum);
+
+ /* Clear miimcom, and then initiate a read */
+ gfar_write(®s->miimcom, 0);
+ gfar_write(®s->miimcom, MII_READ_COMMAND);
+
+ /* Wait for the transaction to finish */
+ while (gfar_read(®s->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
+ cpu_relax();
+
+ /* Grab the value of the register from miimstat */
+ value = gfar_read(®s->miimstat);
+
+ return value;
+}
+
+
+/* Reset the MIIM registers, and wait for the bus to free */
+int gfar_mdio_reset(struct mii_bus *bus)
+{
+ struct gfar_mii *regs = bus->priv;
+ unsigned int timeout = PHY_INIT_TIMEOUT;
+
+ spin_lock_bh(&bus->mdio_lock);
+
+ /* Reset the management interface */
+ gfar_write(®s->miimcfg, MIIMCFG_RESET);
+
+ /* Setup the MII Mgmt clock speed */
+ gfar_write(®s->miimcfg, MIIMCFG_INIT_VALUE);
+
+ /* Wait until the bus is free */
+ while ((gfar_read(®s->miimind) & MIIMIND_BUSY) &&
+ timeout--)
+ cpu_relax();
+
+ spin_unlock_bh(&bus->mdio_lock);
+
+ if(timeout <= 0) {
+ printk(KERN_ERR "%s: The MII Bus is stuck!\n",
+ bus->name);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+
+int gfar_mdio_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gianfar_mdio_data *pdata;
+ struct gfar_mii *regs;
+ struct mii_bus *new_bus;
+ int err = 0;
+
+ printk("probing\n");
+ if (NULL == dev)
+ return -EINVAL;
+
+ new_bus = kmalloc(sizeof(struct mii_bus), GFP_KERNEL);
+
+ if (NULL == new_bus)
+ return -ENOMEM;
+
+ new_bus->name = "Gianfar MII Bus",
+ new_bus->read = &gfar_mdio_read,
+ new_bus->write = &gfar_mdio_write,
+ new_bus->reset = &gfar_mdio_reset,
+ new_bus->id = pdev->id;
+
+ pdata = (struct gianfar_mdio_data *)pdev->dev.platform_data;
+
+ if (NULL == pdata) {
+ printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id);
+ return -ENODEV;
+ }
+
+ /* Set the PHY base address */
+ regs = (struct gfar_mii *) ioremap(pdata->paddr,
+ sizeof (struct gfar_mii));
+
+ if (NULL == regs) {
+ err = -ENOMEM;
+ goto reg_map_fail;
+ }
+
+ new_bus->priv = regs;
+
+ new_bus->irq = pdata->irq;
+
+ new_bus->dev = dev;
+ dev_set_drvdata(dev, new_bus);
+
+ err = mdiobus_register(new_bus);
+
+ if (0 != err) {
+ printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
+ new_bus->name);
+ goto bus_register_fail;
+ }
+
+ return 0;
+
+bus_register_fail:
+ iounmap((void *) regs);
+reg_map_fail:
+ kfree(new_bus);
+
+ return err;
+}
+
+
+int gfar_mdio_remove(struct device *dev)
+{
+ struct mii_bus *bus = dev_get_drvdata(dev);
+
+ mdiobus_unregister(bus);
+
+ dev_set_drvdata(dev, NULL);
+
+ iounmap((void *) (&bus->priv));
+ bus->priv = NULL;
+ kfree(bus);
+
+ return 0;
+}
+
+static struct device_driver gianfar_mdio_driver = {
+ .name = "fsl-gianfar_mdio",
+ .bus = &platform_bus_type,
+ .probe = gfar_mdio_probe,
+ .remove = gfar_mdio_remove,
+};
+
+int __init gfar_mdio_init(void)
+{
+ return driver_register(&gianfar_mdio_driver);
+}
+
+void __exit gfar_mdio_exit(void)
+{
+ driver_unregister(&gianfar_mdio_driver);
+}
diff --git a/drivers/net/gianfar_mii.h b/drivers/net/gianfar_mii.h
new file mode 100644
--- /dev/null
+++ b/drivers/net/gianfar_mii.h
@@ -0,0 +1,45 @@
+/*
+ * drivers/net/gianfar_mii.h
+ *
+ * Gianfar Ethernet Driver -- MII Management Bus Implementation
+ * Driver for the MDIO bus controller in the Gianfar register space
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#ifndef __GIANFAR_MII_H
+#define __GIANFAR_MII_H
+
+#define MIIMIND_BUSY 0x00000001
+#define MIIMIND_NOTVALID 0x00000004
+
+#define MII_READ_COMMAND 0x00000001
+
+#define GFAR_SUPPORTED (SUPPORTED_10baseT_Half \
+ | SUPPORTED_100baseT_Half \
+ | SUPPORTED_100baseT_Full \
+ | SUPPORTED_Autoneg \
+ | SUPPORTED_MII)
+
+struct gfar_mii {
+ u32 miimcfg; /* 0x.520 - MII Management Config Register */
+ u32 miimcom; /* 0x.524 - MII Management Command Register */
+ u32 miimadd; /* 0x.528 - MII Management Address Register */
+ u32 miimcon; /* 0x.52c - MII Management Control Register */
+ u32 miimstat; /* 0x.530 - MII Management Status Register */
+ u32 miimind; /* 0x.534 - MII Management Indicator Register */
+};
+
+int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
+int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
+int __init gfar_mdio_init(void);
+void __exit gfar_mdio_exit(void);
+#endif /* GIANFAR_PHY_H */
diff --git a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c
deleted file mode 100644
--- a/drivers/net/gianfar_phy.c
+++ /dev/null
@@ -1,661 +0,0 @@
-/*
- * drivers/net/gianfar_phy.c
- *
- * Gianfar Ethernet Driver -- PHY handling
- * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
- * Based on 8260_io/fcc_enet.c
- *
- * Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
- *
- * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/crc32.h>
-#include <linux/mii.h>
-
-#include "gianfar.h"
-#include "gianfar_phy.h"
-
-static void config_genmii_advert(struct gfar_mii_info *mii_info);
-static void genmii_setup_forced(struct gfar_mii_info *mii_info);
-static void genmii_restart_aneg(struct gfar_mii_info *mii_info);
-static int gbit_config_aneg(struct gfar_mii_info *mii_info);
-static int genmii_config_aneg(struct gfar_mii_info *mii_info);
-static int genmii_update_link(struct gfar_mii_info *mii_info);
-static int genmii_read_status(struct gfar_mii_info *mii_info);
-u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);
-void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);
-
-/* Write value to the PHY for this device to the register at regnum, */
-/* waiting until the write is done before it returns. All PHY */
-/* configuration has to be done through the TSEC1 MIIM regs */
-void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regbase = priv->phyregs;
-
- /* Set the PHY address and the register address we want to write */
- gfar_write(®base->miimadd, (mii_id << 8) | regnum);
-
- /* Write out the value we want */
- gfar_write(®base->miimcon, value);
-
- /* Wait for the transaction to finish */
- while (gfar_read(®base->miimind) & MIIMIND_BUSY)
- cpu_relax();
-}
-
-/* Reads from register regnum in the PHY for device dev, */
-/* returning the value. Clears miimcom first. All PHY */
-/* configuration has to be done through the TSEC1 MIIM regs */
-int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regbase = priv->phyregs;
- u16 value;
-
- /* Set the PHY address and the register address we want to read */
- gfar_write(®base->miimadd, (mii_id << 8) | regnum);
-
- /* Clear miimcom, and then initiate a read */
- gfar_write(®base->miimcom, 0);
- gfar_write(®base->miimcom, MII_READ_COMMAND);
-
- /* Wait for the transaction to finish */
- while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
- cpu_relax();
-
- /* Grab the value of the register from miimstat */
- value = gfar_read(®base->miimstat);
-
- return value;
-}
-
-void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
-{
- if(mii_info->phyinfo->ack_interrupt)
- mii_info->phyinfo->ack_interrupt(mii_info);
-}
-
-
-void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts)
-{
- mii_info->interrupts = interrupts;
- if(mii_info->phyinfo->config_intr)
- mii_info->phyinfo->config_intr(mii_info);
-}
-
-
-/* Writes MII_ADVERTISE with the appropriate values, after
- * sanitizing advertise to make sure only supported features
- * are advertised
- */
-static void config_genmii_advert(struct gfar_mii_info *mii_info)
-{
- u32 advertise;
- u16 adv;
-
- /* Only allow advertising what this PHY supports */
- mii_info->advertising &= mii_info->phyinfo->features;
- advertise = mii_info->advertising;
-
- /* Setup standard advertisement */
- adv = phy_read(mii_info, MII_ADVERTISE);
- adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
- if (advertise & ADVERTISED_10baseT_Half)
- adv |= ADVERTISE_10HALF;
- if (advertise & ADVERTISED_10baseT_Full)
- adv |= ADVERTISE_10FULL;
- if (advertise & ADVERTISED_100baseT_Half)
- adv |= ADVERTISE_100HALF;
- if (advertise & ADVERTISED_100baseT_Full)
- adv |= ADVERTISE_100FULL;
- phy_write(mii_info, MII_ADVERTISE, adv);
-}
-
-static void genmii_setup_forced(struct gfar_mii_info *mii_info)
-{
- u16 ctrl;
- u32 features = mii_info->phyinfo->features;
-
- ctrl = phy_read(mii_info, MII_BMCR);
-
- ctrl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);
- ctrl |= BMCR_RESET;
-
- switch(mii_info->speed) {
- case SPEED_1000:
- if(features & (SUPPORTED_1000baseT_Half
- | SUPPORTED_1000baseT_Full)) {
- ctrl |= BMCR_SPEED1000;
- break;
- }
- mii_info->speed = SPEED_100;
- case SPEED_100:
- if (features & (SUPPORTED_100baseT_Half
- | SUPPORTED_100baseT_Full)) {
- ctrl |= BMCR_SPEED100;
- break;
- }
- mii_info->speed = SPEED_10;
- case SPEED_10:
- if (features & (SUPPORTED_10baseT_Half
- | SUPPORTED_10baseT_Full))
- break;
- default: /* Unsupported speed! */
- printk(KERN_ERR "%s: Bad speed!\n",
- mii_info->dev->name);
- break;
- }
-
- phy_write(mii_info, MII_BMCR, ctrl);
-}
-
-
-/* Enable and Restart Autonegotiation */
-static void genmii_restart_aneg(struct gfar_mii_info *mii_info)
-{
- u16 ctl;
-
- ctl = phy_read(mii_info, MII_BMCR);
- ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
- phy_write(mii_info, MII_BMCR, ctl);
-}
-
-
-static int gbit_config_aneg(struct gfar_mii_info *mii_info)
-{
- u16 adv;
- u32 advertise;
-
- if(mii_info->autoneg) {
- /* Configure the ADVERTISE register */
- config_genmii_advert(mii_info);
- advertise = mii_info->advertising;
-
- adv = phy_read(mii_info, MII_1000BASETCONTROL);
- adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
- MII_1000BASETCONTROL_HALFDUPLEXCAP);
- if (advertise & SUPPORTED_1000baseT_Half)
- adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
- if (advertise & SUPPORTED_1000baseT_Full)
- adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
- phy_write(mii_info, MII_1000BASETCONTROL, adv);
-
- /* Start/Restart aneg */
- genmii_restart_aneg(mii_info);
- } else
- genmii_setup_forced(mii_info);
-
- return 0;
-}
-
-static int marvell_config_aneg(struct gfar_mii_info *mii_info)
-{
- /* The Marvell PHY has an errata which requires
- * that certain registers get written in order
- * to restart autonegotiation */
- phy_write(mii_info, MII_BMCR, BMCR_RESET);
-
- phy_write(mii_info, 0x1d, 0x1f);
- phy_write(mii_info, 0x1e, 0x200c);
- phy_write(mii_info, 0x1d, 0x5);
- phy_write(mii_info, 0x1e, 0);
- phy_write(mii_info, 0x1e, 0x100);
-
- gbit_config_aneg(mii_info);
-
- return 0;
-}
-static int genmii_config_aneg(struct gfar_mii_info *mii_info)
-{
- if (mii_info->autoneg) {
- config_genmii_advert(mii_info);
- genmii_restart_aneg(mii_info);
- } else
- genmii_setup_forced(mii_info);
-
- return 0;
-}
-
-
-static int genmii_update_link(struct gfar_mii_info *mii_info)
-{
- u16 status;
-
- /* Do a fake read */
- phy_read(mii_info, MII_BMSR);
-
- /* Read link and autonegotiation status */
- status = phy_read(mii_info, MII_BMSR);
- if ((status & BMSR_LSTATUS) == 0)
- mii_info->link = 0;
- else
- mii_info->link = 1;
-
- /* If we are autonegotiating, and not done,
- * return an error */
- if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
- return -EAGAIN;
-
- return 0;
-}
-
-static int genmii_read_status(struct gfar_mii_info *mii_info)
-{
- u16 status;
- int err;
-
- /* Update the link, but return if there
- * was an error */
- err = genmii_update_link(mii_info);
- if (err)
- return err;
-
- if (mii_info->autoneg) {
- status = phy_read(mii_info, MII_LPA);
-
- if (status & (LPA_10FULL | LPA_100FULL))
- mii_info->duplex = DUPLEX_FULL;
- else
- mii_info->duplex = DUPLEX_HALF;
- if (status & (LPA_100FULL | LPA_100HALF))
- mii_info->speed = SPEED_100;
- else
- mii_info->speed = SPEED_10;
- mii_info->pause = 0;
- }
- /* On non-aneg, we assume what we put in BMCR is the speed,
- * though magic-aneg shouldn't prevent this case from occurring
- */
-
- return 0;
-}
-static int marvell_read_status(struct gfar_mii_info *mii_info)
-{
- u16 status;
- int err;
-
- /* Update the link, but return if there
- * was an error */
- err = genmii_update_link(mii_info);
- if (err)
- return err;
-
- /* If the link is up, read the speed and duplex */
- /* If we aren't autonegotiating, assume speeds
- * are as set */
- if (mii_info->autoneg && mii_info->link) {
- int speed;
- status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
-
-#if 0
- /* If speed and duplex aren't resolved,
- * return an error. Isn't this handled
- * by checking aneg?
- */
- if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
- return -EAGAIN;
-#endif
-
- /* Get the duplexity */
- if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
- mii_info->duplex = DUPLEX_FULL;
- else
- mii_info->duplex = DUPLEX_HALF;
-
- /* Get the speed */
- speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
- switch(speed) {
- case MII_M1011_PHY_SPEC_STATUS_1000:
- mii_info->speed = SPEED_1000;
- break;
- case MII_M1011_PHY_SPEC_STATUS_100:
- mii_info->speed = SPEED_100;
- break;
- default:
- mii_info->speed = SPEED_10;
- break;
- }
- mii_info->pause = 0;
- }
-
- return 0;
-}
-
-
-static int cis820x_read_status(struct gfar_mii_info *mii_info)
-{
- u16 status;
- int err;
-
- /* Update the link, but return if there
- * was an error */
- err = genmii_update_link(mii_info);
- if (err)
- return err;
-
- /* If the link is up, read the speed and duplex */
- /* If we aren't autonegotiating, assume speeds
- * are as set */
- if (mii_info->autoneg && mii_info->link) {
- int speed;
-
- status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
- if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
- mii_info->duplex = DUPLEX_FULL;
- else
- mii_info->duplex = DUPLEX_HALF;
-
- speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
-
- switch (speed) {
- case MII_CIS8201_AUXCONSTAT_GBIT:
- mii_info->speed = SPEED_1000;
- break;
- case MII_CIS8201_AUXCONSTAT_100:
- mii_info->speed = SPEED_100;
- break;
- default:
- mii_info->speed = SPEED_10;
- break;
- }
- }
-
- return 0;
-}
-
-static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
-{
- /* Clear the interrupts by reading the reg */
- phy_read(mii_info, MII_M1011_IEVENT);
-
- return 0;
-}
-
-static int marvell_config_intr(struct gfar_mii_info *mii_info)
-{
- if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
- phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
- else
- phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
-
- return 0;
-}
-
-static int cis820x_init(struct gfar_mii_info *mii_info)
-{
- phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
- MII_CIS8201_AUXCONSTAT_INIT);
- phy_write(mii_info, MII_CIS8201_EXT_CON1,
- MII_CIS8201_EXTCON1_INIT);
-
- return 0;
-}
-
-static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
-{
- phy_read(mii_info, MII_CIS8201_ISTAT);
-
- return 0;
-}
-
-static int cis820x_config_intr(struct gfar_mii_info *mii_info)
-{
- if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
- phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
- else
- phy_write(mii_info, MII_CIS8201_IMASK, 0);
-
- return 0;
-}
-
-#define DM9161_DELAY 10
-
-static int dm9161_read_status(struct gfar_mii_info *mii_info)
-{
- u16 status;
- int err;
-
- /* Update the link, but return if there
- * was an error */
- err = genmii_update_link(mii_info);
- if (err)
- return err;
-
- /* If the link is up, read the speed and duplex */
- /* If we aren't autonegotiating, assume speeds
- * are as set */
- if (mii_info->autoneg && mii_info->link) {
- status = phy_read(mii_info, MII_DM9161_SCSR);
- if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
- mii_info->speed = SPEED_100;
- else
- mii_info->speed = SPEED_10;
-
- if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
- mii_info->duplex = DUPLEX_FULL;
- else
- mii_info->duplex = DUPLEX_HALF;
- }
-
- return 0;
-}
-
-
-static int dm9161_config_aneg(struct gfar_mii_info *mii_info)
-{
- struct dm9161_private *priv = mii_info->priv;
-
- if(0 == priv->resetdone)
- return -EAGAIN;
-
- return 0;
-}
-
-static void dm9161_timer(unsigned long data)
-{
- struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
- struct dm9161_private *priv = mii_info->priv;
- u16 status = phy_read(mii_info, MII_BMSR);
-
- if (status & BMSR_ANEGCOMPLETE) {
- priv->resetdone = 1;
- } else
- mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
-}
-
-static int dm9161_init(struct gfar_mii_info *mii_info)
-{
- struct dm9161_private *priv;
-
- /* Allocate the private data structure */
- priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
-
- if (NULL == priv)
- return -ENOMEM;
-
- mii_info->priv = priv;
-
- /* Reset is not done yet */
- priv->resetdone = 0;
-
- /* Isolate the PHY */
- phy_write(mii_info, MII_BMCR, BMCR_ISOLATE);
-
- /* Do not bypass the scrambler/descrambler */
- phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
-
- /* Clear 10BTCSR to default */
- phy_write(mii_info, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
-
- /* Reconnect the PHY, and enable Autonegotiation */
- phy_write(mii_info, MII_BMCR, BMCR_ANENABLE);
-
- /* Start a timer for DM9161_DELAY seconds to wait
- * for the PHY to be ready */
- init_timer(&priv->timer);
- priv->timer.function = &dm9161_timer;
- priv->timer.data = (unsigned long) mii_info;
- mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
-
- return 0;
-}
-
-static void dm9161_close(struct gfar_mii_info *mii_info)
-{
- struct dm9161_private *priv = mii_info->priv;
-
- del_timer_sync(&priv->timer);
- kfree(priv);
-}
-
-#if 0
-static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
-{
- phy_read(mii_info, MII_DM9161_INTR);
-
- return 0;
-}
-#endif
-
-/* Cicada 820x */
-static struct phy_info phy_info_cis820x = {
- 0x000fc440,
- "Cicada Cis8204",
- 0x000fffc0,
- .features = MII_GBIT_FEATURES,
- .init = &cis820x_init,
- .config_aneg = &gbit_config_aneg,
- .read_status = &cis820x_read_status,
- .ack_interrupt = &cis820x_ack_interrupt,
- .config_intr = &cis820x_config_intr,
-};
-
-static struct phy_info phy_info_dm9161 = {
- .phy_id = 0x0181b880,
- .name = "Davicom DM9161E",
- .phy_id_mask = 0x0ffffff0,
- .init = dm9161_init,
- .config_aneg = dm9161_config_aneg,
- .read_status = dm9161_read_status,
- .close = dm9161_close,
-};
-
-static struct phy_info phy_info_marvell = {
- .phy_id = 0x01410c00,
- .phy_id_mask = 0xffffff00,
- .name = "Marvell 88E1101/88E1111",
- .features = MII_GBIT_FEATURES,
- .config_aneg = &marvell_config_aneg,
- .read_status = &marvell_read_status,
- .ack_interrupt = &marvell_ack_interrupt,
- .config_intr = &marvell_config_intr,
-};
-
-static struct phy_info phy_info_genmii= {
- .phy_id = 0x00000000,
- .phy_id_mask = 0x00000000,
- .name = "Generic MII",
- .features = MII_BASIC_FEATURES,
- .config_aneg = genmii_config_aneg,
- .read_status = genmii_read_status,
-};
-
-static struct phy_info *phy_info[] = {
- &phy_info_cis820x,
- &phy_info_marvell,
- &phy_info_dm9161,
- &phy_info_genmii,
- NULL
-};
-
-u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum)
-{
- u16 retval;
- unsigned long flags;
-
- spin_lock_irqsave(&mii_info->mdio_lock, flags);
- retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
- spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
-
- return retval;
-}
-
-void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mii_info->mdio_lock, flags);
- mii_info->mdio_write(mii_info->dev,
- mii_info->mii_id,
- regnum, val);
- spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
-}
-
-/* Use the PHY ID registers to determine what type of PHY is attached
- * to device dev. return a struct phy_info structure describing that PHY
- */
-struct phy_info * get_phy_info(struct gfar_mii_info *mii_info)
-{
- u16 phy_reg;
- u32 phy_ID;
- int i;
- struct phy_info *theInfo = NULL;
- struct net_device *dev = mii_info->dev;
-
- /* Grab the bits from PHYIR1, and put them in the upper half */
- phy_reg = phy_read(mii_info, MII_PHYSID1);
- phy_ID = (phy_reg & 0xffff) << 16;
-
- /* Grab the bits from PHYIR2, and put them in the lower half */
- phy_reg = phy_read(mii_info, MII_PHYSID2);
- phy_ID |= (phy_reg & 0xffff);
-
- /* loop through all the known PHY types, and find one that */
- /* matches the ID we read from the PHY. */
- for (i = 0; phy_info[i]; i++)
- if (phy_info[i]->phy_id ==
- (phy_ID & phy_info[i]->phy_id_mask)) {
- theInfo = phy_info[i];
- break;
- }
-
- /* This shouldn't happen, as we have generic PHY support */
- if (theInfo == NULL) {
- printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
- return NULL;
- } else {
- printk("%s: PHY is %s (%x)\n", dev->name, theInfo->name,
- phy_ID);
- }
-
- return theInfo;
-}
diff --git a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
deleted file mode 100644
--- a/drivers/net/gianfar_phy.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * drivers/net/gianfar_phy.h
- *
- * Gianfar Ethernet Driver -- PHY handling
- * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
- * Based on 8260_io/fcc_enet.c
- *
- * Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
- *
- * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-#ifndef __GIANFAR_PHY_H
-#define __GIANFAR_PHY_H
-
-#define MII_end ((u32)-2)
-#define MII_read ((u32)-1)
-
-#define MIIMIND_BUSY 0x00000001
-#define MIIMIND_NOTVALID 0x00000004
-
-#define GFAR_AN_TIMEOUT 2000
-
-/* 1000BT control (Marvell & BCM54xx at least) */
-#define MII_1000BASETCONTROL 0x09
-#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
-#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
-
-/* Cicada Extended Control Register 1 */
-#define MII_CIS8201_EXT_CON1 0x17
-#define MII_CIS8201_EXTCON1_INIT 0x0000
-
-/* Cicada Interrupt Mask Register */
-#define MII_CIS8201_IMASK 0x19
-#define MII_CIS8201_IMASK_IEN 0x8000
-#define MII_CIS8201_IMASK_SPEED 0x4000
-#define MII_CIS8201_IMASK_LINK 0x2000
-#define MII_CIS8201_IMASK_DUPLEX 0x1000
-#define MII_CIS8201_IMASK_MASK 0xf000
-
-/* Cicada Interrupt Status Register */
-#define MII_CIS8201_ISTAT 0x1a
-#define MII_CIS8201_ISTAT_STATUS 0x8000
-#define MII_CIS8201_ISTAT_SPEED 0x4000
-#define MII_CIS8201_ISTAT_LINK 0x2000
-#define MII_CIS8201_ISTAT_DUPLEX 0x1000
-
-/* Cicada Auxiliary Control/Status Register */
-#define MII_CIS8201_AUX_CONSTAT 0x1c
-#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
-#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
-#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
-#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
-#define MII_CIS8201_AUXCONSTAT_100 0x0008
-
-/* 88E1011 PHY Status Register */
-#define MII_M1011_PHY_SPEC_STATUS 0x11
-#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
-#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
-#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
-#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
-#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
-#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
-
-#define MII_M1011_IEVENT 0x13
-#define MII_M1011_IEVENT_CLEAR 0x0000
-
-#define MII_M1011_IMASK 0x12
-#define MII_M1011_IMASK_INIT 0x6400
-#define MII_M1011_IMASK_CLEAR 0x0000
-
-#define MII_DM9161_SCR 0x10
-#define MII_DM9161_SCR_INIT 0x0610
-
-/* DM9161 Specified Configuration and Status Register */
-#define MII_DM9161_SCSR 0x11
-#define MII_DM9161_SCSR_100F 0x8000
-#define MII_DM9161_SCSR_100H 0x4000
-#define MII_DM9161_SCSR_10F 0x2000
-#define MII_DM9161_SCSR_10H 0x1000
-
-/* DM9161 Interrupt Register */
-#define MII_DM9161_INTR 0x15
-#define MII_DM9161_INTR_PEND 0x8000
-#define MII_DM9161_INTR_DPLX_MASK 0x0800
-#define MII_DM9161_INTR_SPD_MASK 0x0400
-#define MII_DM9161_INTR_LINK_MASK 0x0200
-#define MII_DM9161_INTR_MASK 0x0100
-#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
-#define MII_DM9161_INTR_SPD_CHANGE 0x0008
-#define MII_DM9161_INTR_LINK_CHANGE 0x0004
-#define MII_DM9161_INTR_INIT 0x0000
-#define MII_DM9161_INTR_STOP \
-(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
- | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
-
-/* DM9161 10BT Configuration/Status */
-#define MII_DM9161_10BTCSR 0x12
-#define MII_DM9161_10BTCSR_INIT 0x7800
-
-#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
- SUPPORTED_10baseT_Full | \
- SUPPORTED_100baseT_Half | \
- SUPPORTED_100baseT_Full | \
- SUPPORTED_Autoneg | \
- SUPPORTED_TP | \
- SUPPORTED_MII)
-
-#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
- SUPPORTED_1000baseT_Half | \
- SUPPORTED_1000baseT_Full)
-
-#define MII_READ_COMMAND 0x00000001
-
-#define MII_INTERRUPT_DISABLED 0x0
-#define MII_INTERRUPT_ENABLED 0x1
-/* Taken from mii_if_info and sungem_phy.h */
-struct gfar_mii_info {
- /* Information about the PHY type */
- /* And management functions */
- struct phy_info *phyinfo;
-
- /* forced speed & duplex (no autoneg)
- * partner speed & duplex & pause (autoneg)
- */
- int speed;
- int duplex;
- int pause;
-
- /* The most recently read link state */
- int link;
-
- /* Enabled Interrupts */
- u32 interrupts;
-
- u32 advertising;
- int autoneg;
- int mii_id;
-
- /* private data pointer */
- /* For use by PHYs to maintain extra state */
- void *priv;
-
- /* Provided by host chip */
- struct net_device *dev;
-
- /* A lock to ensure that only one thing can read/write
- * the MDIO bus at a time */
- spinlock_t mdio_lock;
-
- /* Provided by ethernet driver */
- int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
- void (*mdio_write) (struct net_device *dev, int mii_id, int reg, int val);
-};
-
-/* struct phy_info: a structure which defines attributes for a PHY
- *
- * id will contain a number which represents the PHY. During
- * startup, the driver will poll the PHY to find out what its
- * UID--as defined by registers 2 and 3--is. The 32-bit result
- * gotten from the PHY will be ANDed with phy_id_mask to
- * discard any bits which may change based on revision numbers
- * unimportant to functionality
- *
- * There are 6 commands which take a gfar_mii_info structure.
- * Each PHY must declare config_aneg, and read_status.
- */
-struct phy_info {
- u32 phy_id;
- char *name;
- unsigned int phy_id_mask;
- u32 features;
-
- /* Called to initialize the PHY */
- int (*init)(struct gfar_mii_info *mii_info);
-
- /* Called to suspend the PHY for power */
- int (*suspend)(struct gfar_mii_info *mii_info);
-
- /* Reconfigures autonegotiation (or disables it) */
- int (*config_aneg)(struct gfar_mii_info *mii_info);
-
- /* Determines the negotiated speed and duplex */
- int (*read_status)(struct gfar_mii_info *mii_info);
-
- /* Clears any pending interrupts */
- int (*ack_interrupt)(struct gfar_mii_info *mii_info);
-
- /* Enables or disables interrupts */
- int (*config_intr)(struct gfar_mii_info *mii_info);
-
- /* Clears up any memory if needed */
- void (*close)(struct gfar_mii_info *mii_info);
-};
-
-struct phy_info *get_phy_info(struct gfar_mii_info *mii_info);
-int read_phy_reg(struct net_device *dev, int mii_id, int regnum);
-void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value);
-void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info);
-void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts);
-
-struct dm9161_private {
- struct timer_list timer;
- int resetdone;
-};
-
-#endif /* GIANFAR_PHY_H */
^ permalink raw reply
* [RFC PATCH 2/4]: PHY Abstraction Layer III (now with more splitiness)
From: Andy Fleming @ 2005-07-25 19:51 UTC (permalink / raw)
To: Netdev, linuxppc-embedded
This patch contains the PHY drivers
diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/cicada.c
@@ -0,0 +1,134 @@
+/*
+ * drivers/net/phy/cicada.c
+ *
+ * Driver for Cicada PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* Cicada Extended Control Register 1 */
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
+
+/* Cicada Interrupt Mask Register */
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
+
+/* Cicada Interrupt Status Register */
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
+
+/* Cicada Auxiliary Control/Status Register */
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
+
+MODULE_DESCRIPTION("Cicadia PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+static int cis820x_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return err;
+}
+
+static int cis820x_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_CIS8201_ISTAT);
+
+ return (err < 0) ? err : 0;
+}
+
+static int cis820x_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_CIS8201_IMASK,
+ MII_CIS8201_IMASK_MASK);
+ else
+ err = phy_write(phydev, MII_CIS8201_IMASK, 0);
+
+ return err;
+}
+
+/* Cicada 820x */
+static struct phy_driver cis8204_driver = {
+ .phy_id = 0x000fc440,
+ .name = "Cicada Cis8204",
+ .phy_id_mask = 0x000fffc0,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = &cis820x_config_init,
+ .config_aneg = &genphy_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init cis8204_init(void)
+{
+ return phy_driver_register(&cis8204_driver);
+}
+
+static void __exit cis8204_exit(void)
+{
+ phy_driver_unregister(&cis8204_driver);
+}
+
+module_init(cis8204_init);
+module_exit(cis8204_exit);
diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/davicom.c
@@ -0,0 +1,195 @@
+/*
+ * drivers/net/phy/davicom.c
+ *
+ * Driver for Davicom PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
+
+/* DM9161 Interrupt Register */
+#define MII_DM9161_INTR 0x15
+#define MII_DM9161_INTR_PEND 0x8000
+#define MII_DM9161_INTR_DPLX_MASK 0x0800
+#define MII_DM9161_INTR_SPD_MASK 0x0400
+#define MII_DM9161_INTR_LINK_MASK 0x0200
+#define MII_DM9161_INTR_MASK 0x0100
+#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
+#define MII_DM9161_INTR_SPD_CHANGE 0x0008
+#define MII_DM9161_INTR_LINK_CHANGE 0x0004
+#define MII_DM9161_INTR_INIT 0x0000
+#define MII_DM9161_INTR_STOP \
+(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
+ | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
+
+MODULE_DESCRIPTION("Davicom PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+
+#define DM9161_DELAY 1
+static int dm9161_config_intr(struct phy_device *phydev)
+{
+ int temp;
+
+ temp = phy_read(phydev, MII_DM9161_INTR);
+
+ if (temp < 0)
+ return temp;
+
+ if(PHY_INTERRUPT_ENABLED == phydev->interrupts )
+ temp &= ~(MII_DM9161_INTR_STOP);
+ else
+ temp |= MII_DM9161_INTR_STOP;
+
+ temp = phy_write(phydev, MII_DM9161_INTR, temp);
+
+ return temp;
+}
+
+static int dm9161_config_aneg(struct phy_device *phydev)
+{
+ int err;
+
+ /* Isolate the PHY */
+ err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
+
+ if (err < 0)
+ return err;
+
+ /* Configure the new settings */
+ err = genphy_config_aneg(phydev);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int dm9161_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ /* Isolate the PHY */
+ err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
+
+ if (err < 0)
+ return err;
+
+ /* Do not bypass the scrambler/descrambler */
+ err = phy_write(phydev, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
+
+ if (err < 0)
+ return err;
+
+ /* Clear 10BTCSR to default */
+ err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
+
+ if (err < 0)
+ return err;
+
+ /* Reconnect the PHY, and enable Autonegotiation */
+ err = phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int dm9161_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_DM9161_INTR);
+
+ return (err < 0) ? err : 0;
+}
+
+static struct phy_driver dm9161_driver = {
+ .phy_id = 0x0181b880,
+ .name = "Davicom DM9161E",
+ .phy_id_mask = 0x0ffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config_init = dm9161_config_init,
+ .config_aneg = dm9161_config_aneg,
+ .read_status = genphy_read_status,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static struct phy_driver dm9131_driver = {
+ .phy_id = 0x00181b80,
+ .name = "Davicom DM9131",
+ .phy_id_mask = 0x0ffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = dm9161_ack_interrupt,
+ .config_intr = dm9161_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init davicom_init(void)
+{
+ int ret;
+
+ ret = phy_driver_register(&dm9161_driver);
+ if (ret)
+ goto err1;
+
+ ret = phy_driver_register(&dm9131_driver);
+ if (ret)
+ goto err2;
+ return 0;
+
+ err2:
+ phy_driver_unregister(&dm9161_driver);
+ err1:
+ return ret;
+}
+
+static void __exit davicom_exit(void)
+{
+ phy_driver_unregister(&dm9161_driver);
+ phy_driver_unregister(&dm9131_driver);
+}
+
+module_init(davicom_init);
+module_exit(davicom_exit);
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/lxt.c
@@ -0,0 +1,179 @@
+/*
+ * drivers/net/phy/lxt.c
+ *
+ * Driver for Intel LXT PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* The Level one LXT970 is used by many boards */
+
+#define MII_LXT970_IER 17 /* Interrupt Enable Register */
+
+#define MII_LXT970_IER_IEN 0x0002
+
+#define MII_LXT970_ISR 18 /* Interrupt Status Register */
+
+#define MII_LXT970_CONFIG 19 /* Configuration Register */
+
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT971 is used on some of my custom boards */
+
+/* register definitions for the 971 */
+#define MII_LXT971_IER 18 /* Interrupt Enable Register */
+#define MII_LXT971_IER_IEN 0x00f2
+
+#define MII_LXT971_ISR 19 /* Interrupt Status Register */
+
+
+MODULE_DESCRIPTION("Intel LXT PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+static int lxt970_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read(phydev, MII_BMSR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_LXT970_ISR);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int lxt970_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN);
+ else
+ err = phy_write(phydev, MII_LXT970_IER, 0);
+
+ return err;
+}
+
+static int lxt970_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_write(phydev, MII_LXT970_CONFIG, 0);
+
+ return err;
+}
+
+
+static int lxt971_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_LXT971_ISR);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int lxt971_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN);
+ else
+ err = phy_write(phydev, MII_LXT971_IER, 0);
+
+ return err;
+}
+
+static struct phy_driver lxt970_driver = {
+ .phy_id = 0x07810000,
+ .name = "LXT970",
+ .phy_id_mask = 0x0fffffff,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = lxt970_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = lxt970_ack_interrupt,
+ .config_intr = lxt970_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static struct phy_driver lxt971_driver = {
+ .phy_id = 0x0001378e,
+ .name = "LXT971",
+ .phy_id_mask = 0x0fffffff,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = lxt971_ack_interrupt,
+ .config_intr = lxt971_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init lxt_init(void)
+{
+ int ret;
+
+ ret = phy_driver_register(&lxt970_driver);
+ if (ret)
+ goto err1;
+
+ ret = phy_driver_register(&lxt971_driver);
+ if (ret)
+ goto err2;
+ return 0;
+
+ err2:
+ phy_driver_unregister(&lxt970_driver);
+ err1:
+ return ret;
+}
+
+static void __exit lxt_exit(void)
+{
+ phy_driver_unregister(&lxt970_driver);
+ phy_driver_unregister(&lxt971_driver);
+}
+
+module_init(lxt_init);
+module_exit(lxt_exit);
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/marvell.c
@@ -0,0 +1,140 @@
+/*
+ * drivers/net/phy/marvell.c
+ *
+ * Driver for Marvell PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
+
+MODULE_DESCRIPTION("Marvell PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+static int marvell_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ /* Clear the interrupts by reading the reg */
+ err = phy_read(phydev, MII_M1011_IEVENT);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int marvell_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
+ else
+ err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+ return err;
+}
+
+static int marvell_config_aneg(struct phy_device *phydev)
+{
+ int err;
+
+ /* The Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation */
+ err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x1f);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x200c);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x5);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x100);
+ if (err < 0)
+ return err;
+
+
+ err = genphy_config_aneg(phydev);
+
+ return err;
+}
+
+
+static struct phy_driver m88e1101_driver = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = &marvell_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init marvell_init(void)
+{
+ return phy_driver_register(&m88e1101_driver);
+}
+
+static void __exit marvell_exit(void)
+{
+ phy_driver_unregister(&m88e1101_driver);
+}
+
+module_init(marvell_init);
+module_exit(marvell_exit);
diff --git a/drivers/net/phy/qsemi.c b/drivers/net/phy/qsemi.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/qsemi.c
@@ -0,0 +1,143 @@
+/*
+ * drivers/net/phy/qsemi.c
+ *
+ * Driver for Quality Semiconductor PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* ------------------------------------------------------------------------- */
+/* The Quality Semiconductor QS6612 is used on the RPX CLLF */
+
+/* register definitions */
+
+#define MII_QS6612_MCR 17 /* Mode Control Register */
+#define MII_QS6612_FTR 27 /* Factory Test Register */
+#define MII_QS6612_MCO 28 /* Misc. Control Register */
+#define MII_QS6612_ISR 29 /* Interrupt Source Register */
+#define MII_QS6612_IMR 30 /* Interrupt Mask Register */
+#define MII_QS6612_IMR_INIT 0x003a
+#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */
+
+#define QS6612_PCR_AN_COMPLETE 0x1000
+#define QS6612_PCR_RLBEN 0x0200
+#define QS6612_PCR_DCREN 0x0100
+#define QS6612_PCR_4B5BEN 0x0040
+#define QS6612_PCR_TX_ISOLATE 0x0020
+#define QS6612_PCR_MLT3_DIS 0x0002
+#define QS6612_PCR_SCRM_DESCRM 0x0001
+
+MODULE_DESCRIPTION("Quality Semiconductor PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+/* Returns 0, unless there's a write error */
+static int qs6612_config_init(struct phy_device *phydev)
+{
+ /* The PHY powers up isolated on the RPX,
+ * so send a command to allow operation.
+ * XXX - My docs indicate this should be 0x0940
+ * ...or something. The current value sets three
+ * reserved bits, bit 11, which specifies it should be
+ * set to one, bit 10, which specifies it should be set
+ * to 0, and bit 7, which doesn't specify. However, my
+ * docs are preliminary, and I will leave it like this
+ * until someone more knowledgable corrects me or it.
+ * -- Andy Fleming
+ */
+ return phy_write(phydev, MII_QS6612_PCR, 0x0dc0);
+}
+
+static int qs6612_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read(phydev, MII_QS6612_ISR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_BMSR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_EXPANSION);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int qs6612_config_intr(struct phy_device *phydev)
+{
+ int err;
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_QS6612_IMR,
+ MII_QS6612_IMR_INIT);
+ else
+ err = phy_write(phydev, MII_QS6612_IMR, 0);
+
+ return err;
+
+}
+
+static struct phy_driver qs6612_driver = {
+ .phy_id = 0x00181440,
+ .name = "QS6612",
+ .phy_id_mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = qs6612_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = qs6612_ack_interrupt,
+ .config_intr = qs6612_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init qs6612_init(void)
+{
+ return phy_driver_register(&qs6612_driver);
+}
+
+static void __exit qs6612_exit(void)
+{
+ phy_driver_unregister(&qs6612_driver);
+}
+
+module_init(qs6612_init);
+module_exit(qs6612_exit);
diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/networking/phy.txt
@@ -0,0 +1,288 @@
+
+-------
+PHY Abstraction Layer
+(Updated 2005-07-21)
+
+Purpose
+
+ Most network devices consist of set of registers which provide an interface
+ to a MAC layer, which communicates with the physical connection through a
+ PHY. The PHY concerns itself with negotiating link parameters with the link
+ partner on the other side of the network connection (typically, an ethernet
+ cable), and provides a register interface to allow drivers to determine what
+ settings were chosen, and to configure what settings are allowed.
+
+ While these devices are distinct from the network devices, and conform to a
+ standard layout for the registers, it has been common practice to integrate
+ the PHY management code with the network driver. This has resulted in large
+ amounts of redundant code. Also, on embedded systems with multiple (and
+ sometimes quite different) ethernet controllers connected to the same
+ management bus, it is difficult to ensure safe use of the bus.
+
+ Since the PHYs are devices, and the management busses through which they are
+ accessed are, in fact, busses, the PHY Abstraction Layer treats them as such.
+ In doing so, it has these goals:
+
+ 1) Increase code-reuse
+ 2) Increase overall code-maintainability
+ 3) Speed development time for new network drivers, and for new systems
+
+ Basically, this layer is meant to provide an interface to PHY devices which
+ allows network driver writers to write as little code as possible, while
+ still providing a full feature set.
+
+The MDIO bus
+
+ Most network devices are connected to a PHY by means of a management bus.
+ Different devices use different busses (though some share common interfaces).
+ In order to take advantage of the PAL, each bus interface needs to be
+ registered as a distinct device.
+
+ 1) read and write functions must be implemented. Their prototypes are:
+
+ int write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
+ int read(struct mii_bus *bus, int mii_id, int regnum);
+
+ mii_id is the address on the bus for the PHY, and regnum is the register
+ number. These functions are guaranteed not to be called from interrupt
+ time, so it is safe for them to block, waiting for an interrupt to signal
+ the operation is complete
+
+ 2) A reset function is necessary. This is used to return the bus to an
+ initialized state.
+
+ 3) A probe function is needed. This function should set up anything the bus
+ driver needs, setup the mii_bus structure, and register with the PAL using
+ mdiobus_register. Similarly, there's a remove function to undo all of
+ that (use mdiobus_unregister).
+
+ 4) Like any driver, the device_driver structure must be configured, and init
+ exit functions are used to register the driver.
+
+ 5) The bus must also be declared somewhere as a device, and registered.
+
+ As an example for how one driver implemented an mdio bus driver, see
+ drivers/net/gianfar_mii.c and arch/ppc/syslib/mpc85xx_devices.c
+
+Connecting to a PHY
+
+ Sometime during startup, the network driver needs to establish a connection
+ between the PHY device, and the network device. At this time, the PHY's bus
+ and drivers need to all have been loaded, so it is ready for the connection.
+ At this point, there are several ways to connect to the PHY:
+
+ 1) The PAL handles everything, and only calls the network driver when
+ the link state changes, so it can react.
+
+ 2) The PAL handles everything except interrupts (usually because the
+ controller has the interrupt registers).
+
+ 3) The PAL handles everything, but checks in with the driver every second,
+ allowing the network driver to react first to any changes before the PAL
+ does.
+
+ 4) The PAL serves only as a library of functions, with the network device
+ manually calling functions to update status, and configure the PHY
+
+
+Letting the PHY Abstraction Layer do Everything
+
+ If you choose option 1 (The hope is that every driver can, but to still be
+ useful to drivers that can't), connecting to the PHY is simple:
+
+ First, you need a function to react to changes in the link state. This
+ function follows this protocol:
+
+ static void adjust_link(struct net_device *dev);
+
+ Next, you need to know the device name of the PHY connected to this device.
+ The name will look something like, "phy0:0", where the first number is the
+ bus id, and the second is the PHY's address on that bus.
+
+ Now, to connect, just call this function:
+
+ phydev = phy_connect(dev, phy_name, &adjust_link, flags);
+
+ phydev is a pointer to the phy_device structure which represents the PHY. If
+ phy_connect is successful, it will return the pointer. dev, here, is the
+ pointer to your net_device. Once done, this function will have started the
+ PHY's software state machine, and registered for the PHY's interrupt, if it
+ has one. The phydev structure will be populated with information about the
+ current state, though the PHY will not yet be truly operational at this
+ point.
+
+ flags is a u32 which can optionally contain phy-specific flags.
+ This is useful if the system has put hardware restrictions on
+ the PHY/controller, of which the PHY needs to be aware.
+
+ Now just make sure that phydev->supported and phydev->advertising have any
+ values pruned from them which don't make sense for your controller (a 10/100
+ controller may be connected to a gigabit capable PHY, so you would need to
+ mask off SUPPORTED_1000baseT*). See include/linux/ethtool.h for definitions
+ for these bitfields. Note that you should not SET any bits, or the PHY may
+ get put into an unsupported state.
+
+ Lastly, once the controller is ready to handle network traffic, you call
+ phy_start(phydev). This tells the PAL that you are ready, and configures the
+ PHY to connect to the network. If you want to handle your own interrupts,
+ just set phydev->irq to PHY_IGNORE_INTERRUPT before you call phy_start.
+ Similarly, if you don't want to use interrupts, set phydev->irq to PHY_POLL.
+
+ When you want to disconnect from the network (even if just briefly), you call
+ phy_stop(phydev).
+
+Keeping Close Tabs on the PAL
+
+ It is possible that the PAL's built-in state machine needs a little help to
+ keep your network device and the PHY properly in sync. If so, you can
+ register a helper function when connecting to the PHY, which will be called
+ every second before the state machine reacts to any changes. To do this, you
+ need to manually call phy_attach() and phy_prepare_link(), and then call
+ phy_start_machine() with the second argument set to point to your special
+ handler.
+
+ Currently there are no examples of how to use this functionality, and testing
+ on it has been limited because the author does not have any drivers which use
+ it (they all use option 1). So Caveat Emptor.
+
+Doing it all yourself
+
+ There's a remote chance that the PAL's built-in state machine cannot track
+ the complex interactions between the PHY and your network device. If this is
+ so, you can simply call phy_attach(), and not call phy_start_machine or
+ phy_prepare_link(). This will mean that phydev->state is entirely yours to
+ handle (phy_start and phy_stop toggle between some of the states, so you
+ might need to avoid them).
+
+ An effort has been made to make sure that useful functionality can be
+ accessed without the state-machine running, and most of these functions are
+ descended from functions which did not interact with a complex state-machine.
+ However, again, no effort has been made so far to test running without the
+ state machine, so tryer beware.
+
+ Here is a brief rundown of the functions:
+
+ int phy_read(struct phy_device *phydev, u16 regnum);
+ int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+
+ Simple read/write primitives. They invoke the bus's read/write function
+ pointers.
+
+ void phy_print_status(struct phy_device *phydev);
+
+ A convenience function to print out the PHY status neatly.
+
+ int phy_clear_interrupt(struct phy_device *phydev);
+ int phy_config_interrupt(struct phy_device *phydev, u32 interrupts);
+
+ Clear the PHY's interrupt, and configure which ones are allowed,
+ respectively. Currently only supports all on, or all off.
+
+ int phy_enable_interrupts(struct phy_device *phydev);
+ int phy_disable_interrupts(struct phy_device *phydev);
+
+ Functions which enable/disable PHY interrupts, clearing them
+ before and after, respectively.
+
+ int phy_start_interrupts(struct phy_device *phydev);
+ int phy_stop_interrupts(struct phy_device *phydev);
+
+ Requests the IRQ for the PHY interrupts, then enables them for
+ start, or disables then frees them for stop.
+
+ struct phy_device * phy_attach(struct net_device *dev, const char *phy_id,
+ u32 flags);
+
+ Attaches a network device to a particular PHY, binding the PHY to a generic
+ driver if none was found during bus initialization. Passes in
+ any phy-specific flags as needed.
+
+ int phy_start_aneg(struct phy_device *phydev);
+
+ Using variables inside the phydev structure, either configures advertising
+ and resets autonegotiation, or disables autonegotiation, and configures
+ forced settings.
+
+ static inline int phy_read_status(struct phy_device *phydev);
+
+ Fills the phydev structure with up-to-date information about the current
+ settings in the PHY.
+
+ void phy_sanitize_settings(struct phy_device *phydev)
+
+ Resolves differences between currently desired settings, and
+ supported settings for the given PHY device. Does not make
+ the changes in the hardware, though.
+
+ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+
+ Ethtool convenience functions.
+
+ int phy_mii_ioctl(struct phy_device *phydev,
+ struct mii_ioctl_data *mii_data, int cmd);
+
+ The MII ioctl. Note that this function will completely screw up the state
+ machine if you write registers like BMCR, BMSR, ADVERTISE, etc. Best to
+ use this only to write registers which are not standard, and don't set off
+ a renegotiation.
+
+
+PHY Device Drivers
+
+ With the PHY Abstraction Layer, adding support for new PHYs is
+ quite easy. In some cases, no work is required at all! However,
+ many PHYs require a little hand-holding to get up-and-running.
+
+Generic PHY driver
+
+ If the desired PHY doesn't have any errata, quirks, or special
+ features you want to support, then it may be best to not add
+ support, and let the PHY Abstraction Layer's Generic PHY Driver
+ do all of the work.
+
+Writing a PHY driver
+
+ If you do need to write a PHY driver, the first thing to do is
+ make sure it can be matched with an appropriate PHY device.
+ This is done during bus initialization by reading the device's
+ UID (stored in registers 2 and 3), then comparing it to each
+ driver's phy_id field by ANDing it with each driver's
+ phy_id_mask field. Also, it needs a name. Here's an example:
+
+ static struct phy_driver dm9161_driver = {
+ .phy_id = 0x0181b880,
+ .name = "Davicom DM9161E",
+ .phy_id_mask = 0x0ffffff0,
+ ...
+ }
+
+ Next, you need to specify what features (speed, duplex, autoneg,
+ etc) your PHY device and driver support. Most PHYs support
+ PHY_BASIC_FEATURES, but you can look in include/mii.h for other
+ features.
+
+ Each driver consists of a number of function pointers:
+
+ config_init: configures PHY into a sane state after a reset.
+ For instance, a Davicom PHY requires descrambling disabled.
+ probe: Does any setup needed by the driver
+ suspend/resume: power management
+ config_aneg: Changes the speed/duplex/negotiation settings
+ read_status: Reads the current speed/duplex/negotiation settings
+ ack_interrupt: Clear a pending interrupt
+ config_intr: Enable or disable interrupts
+ remove: Does any driver take-down
+
+ Of these, only config_aneg and read_status are required to be
+ assigned by the driver code. The rest are optional. Also, it is
+ preferred to use the generic phy driver's versions of these two
+ functions if at all possible: genphy_read_status and
+ genphy_config_aneg. If this is not possible, it is likely that
+ you only need to perform some actions before and after invoking
+ these functions, and so your functions will wrap the generic
+ ones.
+
+ Feel free to look at the Marvell, Cicada, and Davicom drivers in
+ drivers/net/phy/ for examples (the lxt and qsemi drivers have
+ not been tested as of this writing)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -504,6 +504,7 @@ int phy_enable_interrupts(struct phy_dev
return err;
}
+EXPORT_SYMBOL(phy_enable_interrupts);
/* Disable the PHY interrupts from the PHY side */
int phy_disable_interrupts(struct phy_device *phydev)
@@ -529,6 +530,7 @@ phy_err:
return err;
}
+EXPORT_SYMBOL(phy_disable_interrupts);
/* phy_start_interrupts
*
^ permalink raw reply
* [RFC PATCH 1/4] PHY Abstraction Layer III (now with more splitiness)
From: Andy Fleming @ 2005-07-25 19:47 UTC (permalink / raw)
To: Netdev; +Cc: linuxppc-embedded
This patch contains the PHY layer itself, no phy drivers
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -131,6 +131,8 @@ if NETDEVICES
source "drivers/net/arcnet/Kconfig"
endif
+source "drivers/net/phy/Kconfig"
+
#
# Ethernet
#
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_ADAPTEC_STARFIRE) += starfi
#
obj-$(CONFIG_MII) += mii.o
+obj-$(CONFIG_PHYLIB) += phy/
obj-$(CONFIG_SUNDANCE) += sundance.o
obj-$(CONFIG_HAMACHI) += hamachi.o
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/Kconfig
@@ -0,0 +1,57 @@
+#
+# PHY Layer Configuration
+#
+
+menu "PHY device support"
+
+config PHYLIB
+ bool "PHY Device support and infrastructure"
+ depends on NET_ETHERNET
+ help
+ Ethernet controllers are usually attached to PHY
+ devices. This option provides infrastructure for
+ managing PHY devices.
+
+config PHYCONTROL
+ bool "Support for automatically handling PHY state changes"
+ depends on PHYLIB
+ help
+ Adds code to perform all the work for keeping PHY link
+ state (speed/duplex/etc) up-to-date. Also handles
+ interrupts.
+
+comment "MII PHY device drivers"
+ depends on PHYLIB
+
+config MARVELL_PHY
+ bool "Drivers for Marvell PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently has a driver for the 88E1011S
+
+config DAVICOM_PHY
+ bool "Drivers for Davicom PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports dm9161e and dm9131
+
+config QSEMI_PHY
+ bool "Drivers for Quality Semiconductor PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the qs6612
+
+config LXT_PHY
+ bool "Drivers for the Intel LXT PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the lxt970, lxt971
+
+config CICADA_PHY
+ bool "Drivers for the Cicada PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the cis8204
+
+endmenu
+
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/Makefile
@@ -0,0 +1,9 @@
+# Makefile for Linux PHY drivers
+
+obj-$(CONFIG_PHYLIB) += phy.o phy_device.o mdio_bus.o
+
+obj-$(CONFIG_MARVELL_PHY) += marvell.o
+obj-$(CONFIG_DAVICOM_PHY) += davicom.o
+obj-$(CONFIG_CICADA_PHY) += cicada.o
+obj-$(CONFIG_LXT_PHY) += lxt.o
+obj-$(CONFIG_QSEMI_PHY) += qsemi.o
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/mdio_bus.c
@@ -0,0 +1,175 @@
+/*
+ * drivers/net/phy/mdio_bus.c
+ *
+ * MDIO Bus interface
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
it
+ * under the terms of the GNU General Public License as published by
the
+ * Free Software Foundation; either version 2 of the License, or (at
your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* register_mdiobus
+ *
+ * description: Called by a bus driver to bring up all the PHYs
+ * on a given bus, and attach them to the bus
+ */
+int mdiobus_register(struct mii_bus *bus)
+{
+ int i;
+ int err = 0;
+
+ spin_lock_init(&bus->mdio_lock);
+
+ if (NULL == bus || NULL == bus->name ||
+ NULL == bus->read ||
+ NULL == bus->write)
+ return -EINVAL;
+
+ if (bus->reset)
+ bus->reset(bus);
+
+ for (i=0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *phydev;
+
+ phydev = get_phy_device(bus, i);
+
+ /* There's a PHY at this address
+ * We need to set:
+ * 1) IRQ
+ * 2) bus_id
+ * 3) parent
+ * 4) bus
+ * 5) mii_bus
+ * And, we need to register it */
+ if (phydev) {
+ phydev->irq = bus->irq[i];
+
+ phydev->dev.parent = bus->dev;
+
+ phydev->dev.bus = &mdio_bus_type;
+
+ phydev->bus = bus;
+
+ sprintf(phydev->dev.bus_id, "phy%d:%d", bus->id,
i);
+
+ err = device_register(&phydev->dev);
+
+ if (err)
+ printk("phy %d did not register (%d)\n",
+ i, err);
+
+ /* If get_phy_device returned NULL, it may be
+ * because an error occurred. If so, we return
+ * that error */
+ } else if (errno)
+ return errno;
+
+ bus->phy_map[i] = phydev;
+ }
+
+ pr_info("%s: probed\n", bus->name);
+
+ return err;
+}
+EXPORT_SYMBOL(mdiobus_register);
+
+void mdiobus_unregister(struct mii_bus *bus)
+{
+ int i;
+
+ for (i=0; i < PHY_MAX_ADDR; i++)
+ if (bus->phy_map[i]) {
+ device_unregister(&bus->phy_map[i]->dev);
+ kfree(bus->phy_map[i]);
+ }
+
+}
+EXPORT_SYMBOL(mdiobus_unregister);
+
+/* mdio_bus_match
+ *
+ * description: Given a PHY device, and a PHY driver, return 1 if
+ * the driver supports the device. Otherwise, return 0
+ */
+static int mdio_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct phy_driver *phydrv = to_phy_driver(drv);
+
+ return (phydrv->phy_id == (phydev->phy_id & phydrv->phy_id_mask));
+}
+
+/* Suspend and resume. Copied from platform_suspend and
+ * platform_resume
+ */
+static int mdio_bus_suspend(struct device * dev, u32 state)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->suspend) {
+ ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state,
SUSPEND_SAVE_STATE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state,
SUSPEND_POWER_DOWN);
+ }
+ return ret;
+}
+
+static int mdio_bus_resume(struct device * dev)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->resume) {
+ ret = dev->driver->resume(dev, RESUME_POWER_ON);
+ if (ret == 0)
+ ret = dev->driver->resume(dev,
RESUME_RESTORE_STATE);
+ if (ret == 0)
+ ret = dev->driver->resume(dev, RESUME_ENABLE);
+ }
+ return ret;
+}
+
+struct bus_type mdio_bus_type = {
+ .name = "mdio_bus",
+ .match = mdio_bus_match,
+ .suspend= mdio_bus_suspend,
+ .resume = mdio_bus_resume,
+};
+
+static int __init mdio_bus_init(void)
+{
+ return bus_register(&mdio_bus_type);
+}
+
+subsys_initcall(mdio_bus_init);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/phy.c
@@ -0,0 +1,852 @@
+/*
+ * drivers/net/phy/phy.c
+ *
+ * Framework for configuring and reading PHY devices
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
it
+ * under the terms of the GNU General Public License as published by
the
+ * Free Software Foundation; either version 2 of the License, or (at
your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+int phy_read(struct phy_device *phydev, u16 regnum);
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+void phy_change(void *data);
+void phy_timer(unsigned long data);
+
+/* Convenience function to print out the current phy status
+ */
+void phy_print_status(struct phy_device *phydev)
+{
+ pr_info("%s: Link is %s", phydev->dev.bus_id,
+ phydev->link ? "Up" : "Down");
+ if (phydev->link)
+ printk(" - %d/%s", phydev->speed,
+ DUPLEX_FULL == phydev->duplex ?
+ "Full" : "Half");
+
+ printk("\n");
+}
+EXPORT_SYMBOL(phy_print_status);
+
+
+/* Convenience functions for reading/writing a given PHY
+ * register. They MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation. */
+int phy_read(struct phy_device *phydev, u16 regnum)
+{
+ int retval;
+ struct mii_bus *bus = phydev->bus;
+
+ spin_lock_bh(&bus->mdio_lock);
+ retval = bus->read(bus, phydev->addr, regnum);
+ spin_unlock_bh(&bus->mdio_lock);
+
+ return retval;
+}
+EXPORT_SYMBOL(phy_read);
+
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val)
+{
+ int err;
+ struct mii_bus *bus = phydev->bus;
+
+ spin_lock_bh(&bus->mdio_lock);
+ err = bus->write(bus, phydev->addr, regnum, val);
+ spin_unlock_bh(&bus->mdio_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_write);
+
+
+int phy_clear_interrupt(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (phydev->drv->ack_interrupt)
+ err = phydev->drv->ack_interrupt(phydev);
+
+ return err;
+}
+
+
+int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
+{
+ int err = 0;
+
+ phydev->interrupts = interrupts;
+ if (phydev->drv->config_intr)
+ err = phydev->drv->config_intr(phydev);
+
+ return err;
+}
+
+
+/* phy_aneg_done
+ *
+ * description: Reads the status register and returns 0 either if
+ * auto-negotiation is incomplete, or if there was an error.
+ * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
+ */
+static inline int phy_aneg_done(struct phy_device *phydev)
+{
+ int retval;
+
+ retval = phy_read(phydev, MII_BMSR);
+
+ if (retval < 0)
+ return retval;
+
+ return retval & BMSR_ANEGCOMPLETE;
+}
+
+/* phy_start_aneg
+ *
+ * description: Calls the PHY driver's config_aneg, and then
+ * sets the PHY state to PHY_AN if auto-negotiation is enabled,
+ * and to PHY_FORCING if auto-negotiation is disabled. Unless
+ * the PHY is currently HALTED.
+ */
+int phy_start_aneg(struct phy_device *phydev)
+{
+ int err = 0;
+
+ spin_lock(&phydev->lock);
+
+ if (AUTONEG_DISABLE == phydev->autoneg)
+ phy_sanitize_settings(phydev);
+
+ err = phydev->drv->config_aneg(phydev);
+
+ if (err < 0)
+ return err;
+
+ if (phydev->state != PHY_HALTED) {
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ phydev->state = PHY_AN;
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+ } else {
+ phydev->state = PHY_FORCING;
+ phydev->link_timeout = PHY_FORCE_TIMEOUT;
+ }
+ }
+
+ spin_unlock(&phydev->lock);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_start_aneg);
+
+
+/* A structure for mapping a particular speed and duplex
+ * combination to a particular SUPPORTED and ADVERTISED value */
+struct phy_setting {
+ int speed;
+ int duplex;
+ u32 setting;
+};
+
+/* A mapping of all SUPPORTED settings to speed/duplex */
+static struct phy_setting settings[] = {
+ { .speed = 10000, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_10000baseT_Full,
+ },
+ { .speed = SPEED_1000, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_1000baseT_Full,
+ },
+ { .speed = SPEED_1000, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_1000baseT_Half,
+ },
+ { .speed = SPEED_100, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_100baseT_Full,
+ },
+ { .speed = SPEED_100, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_100baseT_Half,
+ },
+ { .speed = SPEED_10, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_10baseT_Full,
+ },
+ { .speed = SPEED_10, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_10baseT_Half,
+ },
+};
+
+#define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting))
+
+/* phy_find_setting
+ *
+ * description: Searches the settings array for the setting which
+ * matches the desired speed and duplex, and returns the index
+ * of that setting. Returns the index of the last setting if
+ * none of the others match.
+ */
+static inline int phy_find_setting(int speed, int duplex)
+{
+ int idx = 0;
+
+ while (idx < MAX_NUM_SETTINGS &&
+ (settings[idx].speed != speed ||
+ settings[idx].duplex != duplex))
+ idx++;
+
+ return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
+}
+
+/* phy_find_valid
+ * idx: The first index in settings[] to search
+ * features: A mask of the valid settings
+ *
+ * description: Returns the index of the first valid setting less
+ * than or equal to the one pointed to by idx, as determined by
+ * the mask in features. Returns the index of the last setting
+ * if nothing else matches.
+ */
+static inline int phy_find_valid(int idx, u32 features)
+{
+ while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting &
features))
+ idx++;
+
+ return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
+}
+
+/* phy_sanitize_settings
+ *
+ * description: Make sure the PHY is set to supported speeds and
+ * duplexes. Drop down by one in this order: 1000/FULL,
+ * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF
+ */
+void phy_sanitize_settings(struct phy_device *phydev)
+{
+ u32 features = phydev->supported;
+ int idx;
+
+ /* Sanitize settings based on PHY capabilities */
+ if ((features & SUPPORTED_Autoneg) == 0)
+ phydev->autoneg = 0;
+
+ idx = phy_find_valid(phy_find_setting(phydev->speed,
phydev->duplex),
+ features);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+}
+EXPORT_SYMBOL(phy_sanitize_settings);
+
+/* phy_force_reduction
+ *
+ * description: Reduces the speed/duplex settings by
+ * one notch. The order is so:
+ * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF,
+ * 10/FULL, 10/HALF. The function bottoms out at 10/HALF.
+ */
+static void phy_force_reduction(struct phy_device *phydev)
+{
+ int idx;
+
+ idx = phy_find_setting(phydev->speed, phydev->duplex);
+
+ idx++;
+
+ idx = phy_find_valid(idx, phydev->supported);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+
+ pr_info("Trying %d/%s\n", phydev->speed,
+ DUPLEX_FULL == phydev->duplex ?
+ "FULL" : "HALF");
+}
+
+/* phy_ethtool_sset:
+ * A generic ethtool sset function. Handles all the details
+ *
+ * A few notes about parameter checking:
+ * - We don't set port or transceiver, so we don't care what they
+ * were set to.
+ * - phy_start_aneg() will make sure forced settings are sane, and
+ * choose the next best ones from the ones selected, so we don't
+ * care if ethtool tries to give us bad values
+ */
+int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
+{
+ if (cmd->phy_address != phydev->addr)
+ return -EINVAL;
+
+ /* We make sure that we don't pass unsupported
+ * values in to the PHY */
+ cmd->advertising &= phydev->supported;
+
+ /* Verify the settings we care about. */
+ if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg !=
AUTONEG_DISABLE)
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0)
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_DISABLE
+ && ((cmd->speed != SPEED_1000
+ && cmd->speed != SPEED_100
+ && cmd->speed != SPEED_10)
+ || (cmd->duplex != DUPLEX_HALF
+ && cmd->duplex != DUPLEX_FULL)))
+ return -EINVAL;
+
+ phydev->autoneg = cmd->autoneg;
+
+ phydev->speed = cmd->speed;
+
+ phydev->advertising = cmd->advertising;
+
+ if (AUTONEG_ENABLE == cmd->autoneg)
+ phydev->advertising |= ADVERTISED_Autoneg;
+ else
+ phydev->advertising &= ~ADVERTISED_Autoneg;
+
+ phydev->duplex = cmd->duplex;
+
+ /* Restart the PHY */
+ phy_start_aneg(phydev);
+
+ return 0;
+}
+
+int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = phydev->supported;
+
+ cmd->advertising = phydev->advertising;
+
+ cmd->speed = phydev->speed;
+ cmd->duplex = phydev->duplex;
+ cmd->port = PORT_MII;
+ cmd->phy_address = phydev->addr;
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->autoneg = phydev->autoneg;
+
+ return 0;
+}
+
+
+/* Note that this function is currently incompatible with the
+ * PHYCONTROL layer. It changes registers without regard to
+ * current state. Use at own risk
+ */
+int phy_mii_ioctl(struct phy_device *phydev,
+ struct mii_ioctl_data *mii_data, int cmd)
+{
+ u16 val = mii_data->val_in;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ mii_data->phy_id = phydev->addr;
+ break;
+ case SIOCGMIIREG:
+ mii_data->val_out = phy_read(phydev, mii_data->reg_num);
+ break;
+
+ case SIOCSMIIREG:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (mii_data->phy_id == phydev->addr) {
+ switch(mii_data->reg_num) {
+ case MII_BMCR:
+ if (val & (BMCR_RESET|BMCR_ANENABLE))
+ phydev->autoneg = AUTONEG_DISABLE;
+ else
+ phydev->autoneg = AUTONEG_ENABLE;
+ if ((!phydev->autoneg) && (val &
BMCR_FULLDPLX))
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+ break;
+ case MII_ADVERTISE:
+ phydev->advertising = val;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+
+ phy_write(phydev, mii_data->reg_num, val);
+
+ if (mii_data->reg_num == MII_BMCR
+ && val & BMCR_RESET
+ && phydev->drv->config_init)
+ phydev->drv->config_init(phydev);
+ break;
+ }
+
+ return 0;
+}
+
+/* phy_start_machine:
+ *
+ * description: The PHY infrastructure can run a state machine
+ * which tracks whether the PHY is starting up, negotiating,
+ * etc. This function starts the timer which tracks the state
+ * of the PHY. If you want to be notified when the state
+ * changes, pass in the callback, otherwise, pass NULL. If you
+ * want to maintain your own state machine, do not call this
+ * function. */
+void phy_start_machine(struct phy_device *phydev,
+ void (*handler)(struct net_device *))
+{
+ phydev->adjust_state = handler;
+
+ init_timer(&phydev->phy_timer);
+ phydev->phy_timer.function = &phy_timer;
+ phydev->phy_timer.data = (unsigned long) phydev;
+ mod_timer(&phydev->phy_timer, jiffies + HZ);
+}
+
+/* phy_stop_machine
+ *
+ * description: Stops the state machine timer, sets the state to
+ * UP (unless it wasn't up yet), and then frees the interrupt,
+ * if it is in use. This function must be called BEFORE
+ * phy_detach.
+ */
+void phy_stop_machine(struct phy_device *phydev)
+{
+ del_timer_sync(&phydev->phy_timer);
+
+ spin_lock(&phydev->lock);
+ if (phydev->state > PHY_UP)
+ phydev->state = PHY_UP;
+ spin_unlock(&phydev->lock);
+
+ if (phydev->irq != PHY_POLL)
+ phy_stop_interrupts(phydev);
+
+ phydev->adjust_state = NULL;
+}
+
+#ifdef CONFIG_PHYCONTROL
+/* phy_error:
+ *
+ * Moves the PHY to the HALTED state in response to a read
+ * or write error, and tells the controller the link is down.
+ * Must not be called from interrupt context, or while the
+ * phydev->lock is held.
+ */
+void phy_error(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+ phydev->state = PHY_HALTED;
+ spin_unlock(&phydev->lock);
+}
+
+/* phy_interrupt
+ *
+ * description: When a PHY interrupt occurs, the handler disables
+ * interrupts, and schedules a work task to clear the interrupt.
+ */
+static irqreturn_t phy_interrupt(int irq, void *phy_dat, struct pt_regs
*regs)
+{
+ struct phy_device *phydev = phy_dat;
+
+ /* The MDIO bus is not allowed to be written in interrupt
+ * context, so we need to disable the irq here. A work
+ * queue will write the PHY to disable and clear the
+ * interrupt, and then reenable the irq line. */
+ disable_irq_nosync(irq);
+
+ schedule_work(&phydev->phy_queue);
+
+ return IRQ_HANDLED;
+}
+
+/* Enable the interrupts from the PHY side */
+int phy_enable_interrupts(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_clear_interrupt(phydev);
+
+ if (err < 0)
+ return err;
+
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
+
+ return err;
+}
+
+/* Disable the PHY interrupts from the PHY side */
+int phy_disable_interrupts(struct phy_device *phydev)
+{
+ int err;
+
+ /* Disable PHY interrupts */
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+
+ if (err)
+ goto phy_err;
+
+ /* Clear the interrupt */
+ err = phy_clear_interrupt(phydev);
+
+ if (err)
+ goto phy_err;
+
+ return 0;
+
+phy_err:
+ phy_error(phydev);
+
+ return err;
+}
+
+/* phy_start_interrupts
+ *
+ * description: Request the interrupt for the given PHY. If
+ * this fails, then we set irq to PHY_POLL.
+ * Otherwise, we enable the interrupts in the PHY.
+ * Returns 0 on success.
+ * This should only be called with a valid IRQ number.
+ */
+int phy_start_interrupts(struct phy_device *phydev)
+{
+ int err = 0;
+
+ INIT_WORK(&phydev->phy_queue, phy_change, phydev);
+
+ if (request_irq(phydev->irq, phy_interrupt,
+ SA_SHIRQ,
+ "phy_interrupt",
+ phydev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ phydev->bus->name,
+ phydev->irq);
+ phydev->irq = PHY_POLL;
+ return 0;
+ }
+
+ err = phy_enable_interrupts(phydev);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_start_interrupts);
+
+int phy_stop_interrupts(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_disable_interrupts(phydev);
+
+ if (err)
+ phy_error(phydev);
+
+ free_irq(phydev->irq, phydev);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_stop_interrupts);
+
+
+/* Scheduled by the phy_interrupt/timer to handle PHY changes */
+void phy_change(void *data)
+{
+ int err;
+ struct phy_device *phydev = data;
+
+ err = phy_disable_interrupts(phydev);
+
+ if (err)
+ goto phy_err;
+
+ spin_lock(&phydev->lock);
+ if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK ==
phydev->state))
+ phydev->state = PHY_CHANGELINK;
+ spin_unlock(&phydev->lock);
+
+ enable_irq(phydev->irq);
+
+ /* Reenable interrupts */
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
+
+ if (err)
+ goto irq_enable_err;
+
+ return;
+
+irq_enable_err:
+ disable_irq(phydev->irq);
+phy_err:
+ phy_error(phydev);
+}
+
+/* Bring down the PHY link, and stop checking the status. */
+void phy_stop(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+
+ if (PHY_HALTED == phydev->state) {
+ spin_unlock(&phydev->lock);
+ return;
+ }
+
+ if (phydev->irq != PHY_POLL) {
+ /* Clear any pending interrupts */
+ phy_clear_interrupt(phydev);
+
+ /* Disable PHY Interrupts */
+ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+ }
+
+ phydev->state = PHY_HALTED;
+
+ spin_unlock(&phydev->lock);
+}
+
+
+/* phy_start
+ *
+ * description: Indicates the attached device's readiness to
+ * handle PHY-related work. Used during startup to start the
+ * PHY, and after a call to phy_stop() to resume operation.
+ * Also used to indicate the MDIO bus has cleared an error
+ * condition.
+ */
+void phy_start(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+
+ switch (phydev->state) {
+ case PHY_STARTING:
+ phydev->state = PHY_PENDING;
+ break;
+ case PHY_READY:
+ phydev->state = PHY_UP;
+ break;
+ case PHY_HALTED:
+ phydev->state = PHY_RESUMING;
+ default:
+ break;
+ }
+ spin_unlock(&phydev->lock);
+}
+EXPORT_SYMBOL(phy_stop);
+EXPORT_SYMBOL(phy_start);
+
+/* PHY timer which handles the state machine */
+void phy_timer(unsigned long data)
+{
+ struct phy_device *phydev = (struct phy_device *)data;
+ int needs_aneg = 0;
+ int err = 0;
+
+ spin_lock(&phydev->lock);
+
+ if (phydev->adjust_state)
+ phydev->adjust_state(phydev->attached_dev);
+
+ switch(phydev->state) {
+ case PHY_DOWN:
+ case PHY_STARTING:
+ case PHY_READY:
+ case PHY_PENDING:
+ break;
+ case PHY_UP:
+ needs_aneg = 1;
+
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+
+ break;
+ case PHY_AN:
+ /* Check if negotiation is done. Break
+ * if there's an error */
+ err = phy_aneg_done(phydev);
+ if (err < 0)
+ break;
+
+ /* If auto-negotiation is done, we change to
+ * either RUNNING, or NOLINK */
+ if (err > 0) {
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+
netif_carrier_on(phydev->attached_dev);
+ } else {
+ phydev->state = PHY_NOLINK;
+
netif_carrier_off(phydev->attached_dev);
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+
+ } else if (0 == phydev->link_timeout--) {
+ /* The counter expired, so either we
+ * switch to forced mode, or the
+ * magic_aneg bit exists, and we try aneg
+ * again */
+ if (!(phydev->drv->flags &
PHY_HAS_MAGICANEG)) {
+ int idx;
+
+ /* We'll start from the
+ * fastest speed, and work
+ * our way down */
+ idx = phy_find_valid(0,
+
phydev->supported);
+
+ phydev->speed =
settings[idx].speed;
+ phydev->duplex =
settings[idx].duplex;
+
+ phydev->autoneg = AUTONEG_DISABLE;
+ phydev->state = PHY_FORCING;
+ phydev->link_timeout =
+ PHY_FORCE_TIMEOUT;
+
+ pr_info("Trying %d/%s\n",
+ phydev->speed,
+ DUPLEX_FULL ==
+ phydev->duplex ?
+ "FULL" : "HALF");
+ }
+
+ needs_aneg = 1;
+ }
+ break;
+ case PHY_NOLINK:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ netif_carrier_on(phydev->attached_dev);
+ phydev->adjust_link(phydev->attached_dev);
+ }
+ break;
+ case PHY_FORCING:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ netif_carrier_on(phydev->attached_dev);
+ } else {
+ if (0 == phydev->link_timeout--) {
+ phy_force_reduction(phydev);
+ needs_aneg = 1;
+ }
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+ break;
+ case PHY_RUNNING:
+ /* Only register a CHANGE if we are
+ * polling */
+ if (PHY_POLL == phydev->irq)
+ phydev->state = PHY_CHANGELINK;
+ break;
+ case PHY_CHANGELINK:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ netif_carrier_on(phydev->attached_dev);
+ } else {
+ phydev->state = PHY_NOLINK;
+ netif_carrier_off(phydev->attached_dev);
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+
+ if (PHY_POLL != phydev->irq)
+ err = phy_config_interrupt(phydev,
+ PHY_INTERRUPT_ENABLED);
+ break;
+ case PHY_HALTED:
+ if (phydev->link) {
+ phydev->link = 0;
+ netif_carrier_off(phydev->attached_dev);
+ phydev->adjust_link(phydev->attached_dev);
+ }
+ break;
+ case PHY_RESUMING:
+
+ err = phy_clear_interrupt(phydev);
+
+ if (err)
+ break;
+
+ err = phy_config_interrupt(phydev,
+ PHY_INTERRUPT_ENABLED);
+
+ if (err)
+ break;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ err = phy_aneg_done(phydev);
+ if (err < 0)
+ break;
+
+ /* err > 0 if AN is done.
+ * Otherwise, it's 0, and we're
+ * still waiting for AN */
+ if (err > 0) {
+ phydev->state = PHY_RUNNING;
+ } else {
+ phydev->state = PHY_AN;
+ phydev->link_timeout =
PHY_AN_TIMEOUT;
+ }
+ } else
+ phydev->state = PHY_RUNNING;
+ break;
+ }
+
+ spin_unlock(&phydev->lock);
+
+ if (needs_aneg)
+ err = phy_start_aneg(phydev);
+
+ if (err < 0)
+ phy_error(phydev);
+
+ mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);
+}
+
+#endif /* CONFIG_PHYCONTROL */
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/phy_device.c
@@ -0,0 +1,700 @@
+/*
+ * drivers/net/phy/phy_device.c
+ *
+ * Framework for finding and configuring PHYs.
+ * Also contains generic PHY driver
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
it
+ * under the terms of the GNU General Public License as published by
the
+ * Free Software Foundation; either version 2 of the License, or (at
your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* get_phy_device
+ *
+ * description: Reads the ID registers of the PHY at addr on the
+ * bus, then allocates and returns the phy_device to
+ * represent it.
+ */
+struct phy_device * get_phy_device(struct mii_bus *bus, uint addr)
+{
+ int phy_reg;
+ u32 phy_id;
+ struct phy_device *dev = NULL;
+
+ errno = 0;
+
+ /* Grab the bits from PHYIR1, and put them
+ * in the upper half */
+ phy_reg = bus->read(bus, addr, MII_PHYSID1);
+
+ if (phy_reg < 0) {
+ errno = phy_reg;
+ return NULL;
+ }
+
+ phy_id = (phy_reg & 0xffff) << 16;
+
+ /* Grab the bits from PHYIR2, and put them in the lower half */
+ phy_reg = bus->read(bus, addr, MII_PHYSID2);
+
+ if (phy_reg < 0) {
+ errno = phy_reg;
+ return NULL;
+ }
+
+ phy_id |= (phy_reg & 0xffff);
+
+ /* If the phy_id is all Fs, there is no device there */
+ if (0xffffffff == phy_id)
+ return NULL;
+
+ /* Otherwise, we allocate the device, and initialize the
+ * default values */
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+ if (NULL == dev) {
+ errno = -ENOMEM;
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->speed = 0;
+ dev->duplex = -1;
+ dev->pause = dev->asym_pause = 0;
+ dev->link = 1;
+
+ dev->autoneg = AUTONEG_ENABLE;
+
+ dev->addr = addr;
+ dev->phy_id = phy_id;
+ dev->bus = bus;
+
+ dev->state = PHY_DOWN;
+
+ spin_lock_init(&dev->lock);
+
+ return dev;
+}
+
+/* phy_prepare_link:
+ *
+ * description: Tells the PHY infrastructure to handle the
+ * gory details on monitoring link status (whether through
+ * polling or an interrupt), and to call back to the
+ * connected device driver when the link status changes.
+ * If you want to monitor your own link state, don't call
+ * this function */
+void phy_prepare_link(struct phy_device *phydev,
+ void (*handler)(struct net_device *))
+{
+ phydev->adjust_link = handler;
+}
+
+#ifdef CONFIG_PHYCONTROL
+/* phy_connect:
+ *
+ * description: Convenience function for connecting ethernet
+ * devices to PHY devices. The default behavior is for
+ * the PHY infrastructure to handle everything, and only notify
+ * the connected driver when the link status changes. If you
+ * don't want, or can't use the provided functionality, you may
+ * choose to call only the subset of functions which provide
+ * the desired functionality.
+ */
+struct phy_device * phy_connect(struct net_device *dev, const char
*phy_id,
+ void (*handler)(struct net_device *), u32 flags)
+{
+ struct phy_device *phydev;
+
+ phydev = phy_attach(dev, phy_id, flags);
+
+ if (NULL == phydev)
+ return phydev;
+
+ phy_prepare_link(phydev, handler);
+
+ phy_start_machine(phydev, NULL);
+
+ if (phydev->irq > 0)
+ phy_start_interrupts(phydev);
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_connect);
+
+void phy_disconnect(struct phy_device *phydev)
+{
+ if (phydev->irq > 0)
+ phy_stop_interrupts(phydev);
+
+ phy_stop_machine(phydev);
+
+ phydev->adjust_link = NULL;
+
+ phy_detach(phydev);
+}
+EXPORT_SYMBOL(phy_disconnect);
+
+#endif /* CONFIG_PHYCONTROL */
+
+/* phy_attach:
+ *
+ * description: Called by drivers to attach to a particular PHY
+ * device. The phy_device is found, and properly hooked up
+ * to the phy_driver. If no driver is attached, then the
+ * genphy_driver is used. The phy_device is given a ptr to
+ * the attaching device, and given a callback for link status
+ * change. The phy_device is returned to the attaching
+ * driver.
+ */
+static int phy_compare_id(struct device *dev, void *data)
+{
+ const char *name = data;
+
+ if (strcmp(name, dev->bus_id) == 0)
+ return 1;
+ return 0;
+}
+
+struct phy_device *phy_attach(struct net_device *dev,
+ const char *phy_id, u32 flags)
+{
+ struct phy_device *phydev = NULL;
+ struct bus_type *bus = &mdio_bus_type;
+ struct device *d;
+
+ /* Search the list of PHY devices on the mdio bus for the
+ * PHY with the requested name */
+ d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id);
+
+ if (d) {
+ phydev = to_phy_device(d);
+ } else {
+ printk(KERN_ERR "%s not found\n", phy_id);
+ errno = -ENODEV;
+ return NULL;
+ }
+
+ /* Assume that if there is no driver, that it doesn't
+ * exist, and we should use the genphy driver. */
+ if (NULL == phydev->dev.driver) {
+ int err;
+ down_write(&phydev->dev.bus->subsys.rwsem);
+ phydev->dev.driver = &genphy_driver.driver;
+
+ err = phydev->dev.driver->probe(&phydev->dev);
+
+ if (err < 0) {
+ errno = err;
+ return NULL;
+ }
+
+ device_bind_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ }
+
+ if (phydev->attached_dev) {
+ printk(KERN_ERR "%s: %s already attached\n",
+ dev->name, phy_id);
+ errno = -EBUSY;
+ return NULL;
+ }
+
+ phydev->attached_dev = dev;
+
+ phydev->dev_flags = flags;
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_attach);
+
+void phy_detach(struct phy_device *phydev)
+{
+ phydev->attached_dev = NULL;
+
+ /* If the device had no specific driver before (i.e. - it
+ * was using the generic driver), we unbind the device
+ * from the generic driver so that there's a chance a
+ * real driver could be loaded */
+ if (phydev->dev.driver == &genphy_driver.driver) {
+ down_write(&phydev->dev.bus->subsys.rwsem);
+ device_release_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ }
+}
+EXPORT_SYMBOL(phy_detach);
+
+
+/* Generic PHY support and helper functions */
+
+/* genphy_config_advert
+ *
+ * description: Writes MII_ADVERTISE with the appropriate values,
+ * after sanitizing the values to make sure we only advertise
+ * what is supported
+ */
+int genphy_config_advert(struct phy_device *phydev)
+{
+ u32 advertise;
+ int adv;
+ int err;
+
+ /* Only allow advertising what
+ * this PHY supports */
+ phydev->advertising &= phydev->supported;
+ advertise = phydev->advertising;
+
+ /* Setup standard advertisement */
+ adv = phy_read(phydev, MII_ADVERTISE);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP
|
+ ADVERTISE_PAUSE_ASYM);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ if (advertise & ADVERTISED_Pause)
+ adv |= ADVERTISE_PAUSE_CAP;
+ if (advertise & ADVERTISED_Asym_Pause)
+ adv |= ADVERTISE_PAUSE_ASYM;
+
+ err = phy_write(phydev, MII_ADVERTISE, adv);
+
+ if (err < 0)
+ return err;
+
+ /* Configure gigabit if it's supported */
+ if (phydev->supported & (SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full)) {
+ adv = phy_read(phydev, MII_CTRL1000);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= ADVERTISE_1000HALF;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= ADVERTISE_1000FULL;
+ err = phy_write(phydev, MII_CTRL1000, adv);
+
+ if (err < 0)
+ return err;
+ }
+
+ return adv;
+}
+EXPORT_SYMBOL(genphy_config_advert);
+
+/* genphy_setup_forced
+ *
+ * description: Configures MII_BMCR to force speed/duplex
+ * to the values in phydev. Assumes that the values are valid.
+ * Please see phy_sanitize_settings() */
+int genphy_setup_forced(struct phy_device *phydev)
+{
+ int ctl = BMCR_RESET;
+
+ phydev->pause = phydev->asym_pause = 0;
+
+ if (SPEED_1000 == phydev->speed)
+ ctl |= BMCR_SPEED1000;
+ else if (SPEED_100 == phydev->speed)
+ ctl |= BMCR_SPEED100;
+
+ if (DUPLEX_FULL == phydev->duplex)
+ ctl |= BMCR_FULLDPLX;
+
+ ctl = phy_write(phydev, MII_BMCR, ctl);
+
+ if (ctl < 0)
+ return ctl;
+
+ /* We just reset the device, so we'd better configure any
+ * settings the PHY requires to operate */
+ if (phydev->drv->config_init)
+ ctl = phydev->drv->config_init(phydev);
+
+ return ctl;
+}
+
+
+/* Enable and Restart Autonegotiation */
+int genphy_restart_aneg(struct phy_device *phydev)
+{
+ int ctl;
+
+ ctl = phy_read(phydev, MII_BMCR);
+
+ if (ctl < 0)
+ return ctl;
+
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+
+ /* Don't isolate the PHY if we're negotiating */
+ ctl &= ~(BMCR_ISOLATE);
+
+ ctl = phy_write(phydev, MII_BMCR, ctl);
+
+ return ctl;
+}
+
+
+/* genphy_config_aneg
+ *
+ * description: If auto-negotiation is enabled, we configure the
+ * advertising, and then restart auto-negotiation. If it is not
+ * enabled, then we write the BMCR
+ */
+int genphy_config_aneg(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ err = genphy_config_advert(phydev);
+
+ if (err < 0)
+ return err;
+
+ err = genphy_restart_aneg(phydev);
+ } else
+ err = genphy_setup_forced(phydev);
+
+ return err;
+}
+EXPORT_SYMBOL(genphy_config_aneg);
+
+/* genphy_update_link
+ *
+ * description: Update the value in phydev->link to reflect the
+ * current link value. In order to do this, we need to read
+ * the status register twice, keeping the second value
+ */
+int genphy_update_link(struct phy_device *phydev)
+{
+ int status;
+
+ /* Do a fake read */
+ status = phy_read(phydev, MII_BMSR);
+
+ if (status < 0)
+ return status;
+
+ /* Read link and autonegotiation status */
+ status = phy_read(phydev, MII_BMSR);
+
+ if (status < 0)
+ return status;
+
+ if ((status & BMSR_LSTATUS) == 0)
+ phydev->link = 0;
+ else
+ phydev->link = 1;
+
+ return 0;
+}
+
+/* genphy_read_status
+ *
+ * description: Check the link, then figure out the current state
+ * by comparing what we advertise with what the link partner
+ * advertises. Start by checking the gigabit possibilities,
+ * then move on to 10/100.
+ */
+int genphy_read_status(struct phy_device *phydev)
+{
+ int adv;
+ int err;
+ int lpa;
+ int lpagb = 0;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genphy_update_link(phydev);
+ if (err)
+ return err;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ if (phydev->supported & (SUPPORTED_1000baseT_Half
+ | SUPPORTED_1000baseT_Full)) {
+ lpagb = phy_read(phydev, MII_STAT1000);
+
+ if (lpagb < 0)
+ return lpagb;
+
+ adv = phy_read(phydev, MII_CTRL1000);
+
+ if (adv < 0)
+ return adv;
+
+ lpagb &= adv << 2;
+ }
+
+ lpa = phy_read(phydev, MII_LPA);
+
+ if (lpa < 0)
+ return lpa;
+
+ adv = phy_read(phydev, MII_ADVERTISE);
+
+ if (adv < 0)
+ return adv;
+
+ lpa &= adv;
+
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+ phydev->pause = phydev->asym_pause = 0;
+
+ if (lpagb & (LPA_1000FULL | LPA_1000HALF)) {
+ phydev->speed = SPEED_1000;
+
+ if (lpagb & LPA_1000FULL)
+ phydev->duplex = DUPLEX_FULL;
+ } else if (lpa & (LPA_100FULL | LPA_100HALF)) {
+ phydev->speed = SPEED_100;
+
+ if (lpa & LPA_100FULL)
+ phydev->duplex = DUPLEX_FULL;
+ } else
+ if (lpa & LPA_10FULL)
+ phydev->duplex = DUPLEX_FULL;
+
+ if (phydev->duplex == DUPLEX_FULL){
+ phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
+ phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
+ }
+ } else {
+ int bmcr = phy_read(phydev, MII_BMCR);
+ if (bmcr < 0)
+ return bmcr;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ phydev->pause = phydev->asym_pause = 0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(genphy_read_status);
+
+static int genphy_config_init(struct phy_device *phydev)
+{
+ u32 val;
+ u32 features;
+
+ /* For now, I'll claim that the generic driver supports
+ * all possible port types */
+ features = (SUPPORTED_TP | SUPPORTED_MII
+ | SUPPORTED_AUI | SUPPORTED_FIBRE |
+ SUPPORTED_BNC);
+
+ /* Do we support autonegotiation? */
+ val = phy_read(phydev, MII_BMSR);
+
+ if (val < 0)
+ return val;
+
+ if (val & BMSR_ANEGCAPABLE)
+ features |= SUPPORTED_Autoneg;
+
+ if (val & BMSR_100FULL)
+ features |= SUPPORTED_100baseT_Full;
+ if (val & BMSR_100HALF)
+ features |= SUPPORTED_100baseT_Half;
+ if (val & BMSR_10FULL)
+ features |= SUPPORTED_10baseT_Full;
+ if (val & BMSR_10HALF)
+ features |= SUPPORTED_10baseT_Half;
+
+ if (val & BMSR_ESTATEN) {
+ val = phy_read(phydev, MII_ESTATUS);
+
+ if (val < 0)
+ return val;
+
+ if (val & ESTATUS_1000_TFULL)
+ features |= SUPPORTED_1000baseT_Full;
+ if (val & ESTATUS_1000_THALF)
+ features |= SUPPORTED_1000baseT_Half;
+ }
+
+ phydev->supported = features;
+ phydev->advertising = features;
+
+ return 0;
+}
+
+
+/* phy_probe
+ *
+ * description: Take care of setting up the phy_device structure,
+ * set the state to READY (the driver's init function should
+ * set it to STARTING if needed).
+ */
+static int phy_probe(struct device *dev)
+{
+ struct phy_device *phydev;
+ struct phy_driver *phydrv;
+ struct device_driver *drv;
+ int err = 0;
+
+ phydev = to_phy_device(dev);
+
+ /* Make sure the driver is held.
+ * XXX -- Is this correct? */
+ drv = get_driver(phydev->dev.driver);
+ phydrv = to_phy_driver(drv);
+ phydev->drv = phydrv;
+
+ /* Disable the interrupt if the PHY doesn't support it */
+ if (!(phydrv->flags & PHY_HAS_INTERRUPT))
+ phydev->irq = PHY_POLL;
+
+ spin_lock(&phydev->lock);
+
+ /* Start out supporting everything. Eventually,
+ * a controller will attach, and may modify one
+ * or both of these values */
+ phydev->supported = phydrv->features;
+ phydev->advertising = phydrv->features;
+
+ /* Set the state to READY by default */
+ phydev->state = PHY_READY;
+
+ if (phydev->drv->probe)
+ err = phydev->drv->probe(phydev);
+
+ spin_unlock(&phydev->lock);
+
+ if (err < 0)
+ return err;
+
+ if (phydev->drv->config_init)
+ err = phydev->drv->config_init(phydev);
+
+ return err;
+}
+
+static int phy_remove(struct device *dev)
+{
+ struct phy_device *phydev;
+
+ phydev = to_phy_device(dev);
+
+ spin_lock(&phydev->lock);
+ phydev->state = PHY_DOWN;
+ spin_unlock(&phydev->lock);
+
+ if (phydev->drv->remove)
+ phydev->drv->remove(phydev);
+
+ put_driver(phydev->dev.driver);
+ phydev->drv = NULL;
+
+ return 0;
+}
+
+int phy_driver_register(struct phy_driver *new_driver)
+{
+ int retval;
+
+ memset(&new_driver->driver, 0, sizeof(new_driver->driver));
+ new_driver->driver.name = new_driver->name;
+ new_driver->driver.bus = &mdio_bus_type;
+ new_driver->driver.probe = phy_probe;
+ new_driver->driver.remove = phy_remove;
+
+ retval = driver_register(&new_driver->driver);
+
+ if (retval) {
+ printk(KERN_ERR "%s: Error %d in registering driver\n",
+ new_driver->name, retval);
+
+ return retval;
+ }
+
+ pr_info("%s: Registered new driver\n", new_driver->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(phy_driver_register);
+
+void phy_driver_unregister(struct phy_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(phy_driver_unregister);
+
+static struct phy_driver genphy_driver = {
+ .phy_id = 0xffffffff,
+ .phy_id_mask = 0xffffffff,
+ .name = "Generic PHY",
+ .config_init = genphy_config_init,
+ .features = 0,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .driver = {.owner = THIS_MODULE, },
+};
+
+static int __init genphy_init(void)
+{
+ return phy_driver_register(&genphy_driver);
+
+}
+
+static void __exit genphy_exit(void)
+{
+ phy_driver_unregister(&genphy_driver);
+}
+
+module_init(genphy_init);
+module_exit(genphy_exit);
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -408,6 +408,8 @@ struct ethtool_ops {
#define SUPPORTED_FIBRE (1 << 10)
#define SUPPORTED_BNC (1 << 11)
#define SUPPORTED_10000baseT_Full (1 << 12)
+#define SUPPORTED_Pause (1 << 13)
+#define SUPPORTED_Asym_Pause (1 << 14)
/* Indicates what features are advertised by the interface. */
#define ADVERTISED_10baseT_Half (1 << 0)
@@ -423,6 +425,8 @@ struct ethtool_ops {
#define ADVERTISED_FIBRE (1 << 10)
#define ADVERTISED_BNC (1 << 11)
#define ADVERTISED_10000baseT_Full (1 << 12)
+#define ADVERTISED_Pause (1 << 13)
+#define ADVERTISED_Asym_Pause (1 << 14)
/* The following are all involved in forcing a particular link
* mode for the device for setting things. When getting the
diff --git a/include/linux/mii.h b/include/linux/mii.h
--- a/include/linux/mii.h
+++ b/include/linux/mii.h
@@ -22,6 +22,7 @@
#define MII_EXPANSION 0x06 /* Expansion register */
#define MII_CTRL1000 0x09 /* 1000BASE-T control */
#define MII_STAT1000 0x0a /* 1000BASE-T status */
+#define MII_ESTATUS 0x0f /* Extended Status */
#define MII_DCOUNTER 0x12 /* Disconnect counter */
#define MII_FCSCOUNTER 0x13 /* False carrier counter */
#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
@@ -54,7 +55,10 @@
#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
#define BMSR_RFAULT 0x0010 /* Remote fault detected */
#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
-#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_RESV 0x00c0 /* Unused... */
+#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */
+#define BMSR_100FULL2 0x0200 /* Can do 100BASE-T2 HDX */
+#define BMSR_100HALF2 0x0400 /* Can do 100BASE-T2 FDX */
#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
@@ -113,6 +117,9 @@
#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */
#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
#define EXPANSION_RESV 0xffe0 /* Unused... */
+
+#define ESTATUS_1000_TFULL 0x2000 /* Can do 1000BT Full */
+#define ESTATUS_1000_THALF 0x1000 /* Can do 1000BT Half */
/* N-way test register. */
#define NWAYTEST_RESV1 0x00ff /* Unused... */
diff --git a/include/linux/phy.h b/include/linux/phy.h
new file mode 100644
--- /dev/null
+++ b/include/linux/phy.h
@@ -0,0 +1,378 @@
+/*
+ * include/linux/phy.h
+ *
+ * Framework and drivers for configuring and reading different PHYs
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
it
+ * under the terms of the GNU General Public License as published by
the
+ * Free Software Foundation; either version 2 of the License, or (at
your
+ * option) any later version.
+ *
+ */
+
+#ifndef __PHY_H
+#define __PHY_H
+
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+/* Set phydev->irq to PHY_POLL if interrupts are not supported,
+ * or not desired for this PHY. Set to PHY_IGNORE_INTERRUPT if
+ * the attached driver handles the interrupt
+ */
+#define PHY_POLL -1
+#define PHY_IGNORE_INTERRUPT -2
+
+#define PHY_HAS_INTERRUPT 0x00000001
+#define PHY_HAS_MAGICANEG 0x00000002
+
+#define MII_BUS_MAX 4
+
+
+#define PHY_INIT_TIMEOUT 100000
+#define PHY_STATE_TIME 1
+#define PHY_FORCE_TIMEOUT 10
+#define PHY_AN_TIMEOUT 10
+
+#define PHY_MAX_ADDR 32
+
+/* The Bus class for PHYs. Devices which provide access to
+ * PHYs should register using this structure */
+struct mii_bus {
+ const char *name;
+ int id;
+ void *priv;
+ int (*read)(struct mii_bus *bus, int phy_id, int regnum);
+ int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16
val);
+ int (*reset)(struct mii_bus *bus);
+
+ /* A lock to ensure that only one thing can read/write
+ * the MDIO bus at a time */
+ spinlock_t mdio_lock;
+
+ struct device *dev;
+
+ /* list of all PHYs on bus */
+ struct phy_device *phy_map[PHY_MAX_ADDR];
+
+ /* Pointer to an array of interrupts, each PHY's
+ * interrupt at the index matching its address */
+ int *irq;
+};
+
+#define PHY_INTERRUPT_DISABLED 0x0
+#define PHY_INTERRUPT_ENABLED 0x80000000
+
+/* PHY state machine states:
+ *
+ * DOWN: PHY device and driver are not ready for anything. probe
+ * should be called if and only if the PHY is in this state,
+ * given that the PHY device exists.
+ * - PHY driver probe function will, depending on the PHY, set
+ * the state to STARTING or READY
+ *
+ * STARTING: PHY device is coming up, and the ethernet driver is
+ * not ready. PHY drivers may set this in the probe function.
+ * If they do, they are responsible for making sure the state is
+ * eventually set to indicate whether the PHY is UP or READY,
+ * depending on the state when the PHY is done starting up.
+ * - PHY driver will set the state to READY
+ * - start will set the state to PENDING
+ *
+ * READY: PHY is ready to send and receive packets, but the
+ * controller is not. By default, PHYs which do not implement
+ * probe will be set to this state by phy_probe(). If the PHY
+ * driver knows the PHY is ready, and the PHY state is STARTING,
+ * then it sets this STATE.
+ * - start will set the state to UP
+ *
+ * PENDING: PHY device is coming up, but the ethernet driver is
+ * ready. phy_start will set this state if the PHY state is
+ * STARTING.
+ * - PHY driver will set the state to UP when the PHY is ready
+ *
+ * UP: The PHY and attached device are ready to do work.
+ * Interrupts should be started here.
+ * - timer moves to AN
+ *
+ * AN: The PHY is currently negotiating the link state. Link is
+ * therefore down for now. phy_timer will set this state when it
+ * detects the state is UP. config_aneg will set this state
+ * whenever called with phydev->autoneg set to AUTONEG_ENABLE.
+ * - If autonegotiation finishes, but there's no link, it sets
+ * the state to NOLINK.
+ * - If aneg finishes with link, it sets the state to RUNNING,
+ * and calls adjust_link
+ * - If autonegotiation did not finish after an arbitrary amount
+ * of time, autonegotiation should be tried again if the PHY
+ * supports "magic" autonegotiation (back to AN)
+ * - If it didn't finish, and no magic_aneg, move to FORCING.
+ *
+ * NOLINK: PHY is up, but not currently plugged in.
+ * - If the timer notes that the link comes back, we move to RUNNING
+ * - config_aneg moves to AN
+ * - phy_stop moves to HALTED
+ *
+ * FORCING: PHY is being configured with forced settings
+ * - if link is up, move to RUNNING
+ * - If link is down, we drop to the next highest setting, and
+ * retry (FORCING) after a timeout
+ * - phy_stop moves to HALTED
+ *
+ * RUNNING: PHY is currently up, running, and possibly sending
+ * and/or receiving packets
+ * - timer will set CHANGELINK if we're polling (this ensures the
+ * link state is polled every other cycle of this state machine,
+ * which makes it every other second)
+ * - irq will set CHANGELINK
+ * - config_aneg will set AN
+ * - phy_stop moves to HALTED
+ *
+ * CHANGELINK: PHY experienced a change in link state
+ * - timer moves to RUNNING if link
+ * - timer moves to NOLINK if the link is down
+ * - phy_stop moves to HALTED
+ *
+ * HALTED: PHY is up, but no polling or interrupts are done. Or
+ * PHY is in an error state.
+ *
+ * - phy_start moves to RESUMING
+ *
+ * RESUMING: PHY was halted, but now wants to run again.
+ * - If we are forcing, or aneg is done, timer moves to RUNNING
+ * - If aneg is not done, timer moves to AN
+ * - phy_stop moves to HALTED
+ */
+enum phy_state {
+ PHY_DOWN=0,
+ PHY_STARTING,
+ PHY_READY,
+ PHY_PENDING,
+ PHY_UP,
+ PHY_AN,
+ PHY_RUNNING,
+ PHY_NOLINK,
+ PHY_FORCING,
+ PHY_CHANGELINK,
+ PHY_HALTED,
+ PHY_RESUMING
+};
+
+/* phy_device: An instance of a PHY
+ *
+ * drv: Pointer to the driver for this PHY instance
+ * bus: Pointer to the bus this PHY is on
+ * dev: driver model device structure for this PHY
+ * phy_id: UID for this device found during discovery
+ * state: state of the PHY for management purposes
+ * dev_flags: Device-specific flags used by the PHY driver.
+ * addr: Bus address of PHY
+ * link_timeout: The number of timer firings to wait before the
+ * giving up on the current attempt at acquiring a link
+ * irq: IRQ number of the PHY's interrupt (-1 if none)
+ * phy_timer: The timer for handling the state machine
+ * phy_queue: A work_queue for the interrupt
+ * attached_dev: The attached enet driver's device instance ptr
+ * adjust_link: Callback for the enet controller to respond to
+ * changes in the link state.
+ * adjust_state: Callback for the enet driver to respond to
+ * changes in the state machine.
+ *
+ * speed, duplex, pause, supported, advertising, and
+ * autoneg are used like in mii_if_info
+ *
+ * interrupts currently only supports enabled or disabled,
+ * but could be changed in the future to support enabling
+ * and disabling specific interrupts
+ *
+ * Contains some infrastructure for polling and interrupt
+ * handling, as well as handling shifts in PHY hardware state
+ */
+struct phy_device {
+ /* Information about the PHY type */
+ /* And management functions */
+ struct phy_driver *drv;
+
+ struct mii_bus *bus;
+
+ struct device dev;
+
+ u32 phy_id;
+
+ enum phy_state state;
+
+ u32 dev_flags;
+
+ /* Bus address of the PHY (0-32) */
+ int addr;
+
+ /* forced speed & duplex (no autoneg)
+ * partner speed & duplex & pause (autoneg)
+ */
+ int speed;
+ int duplex;
+ int pause;
+ int asym_pause;
+
+ /* The most recently read link state */
+ int link;
+
+ /* Enabled Interrupts */
+ u32 interrupts;
+
+ /* Union of PHY and Attached devices' supported modes */
+ /* See mii.h for more info */
+ u32 supported;
+ u32 advertising;
+
+ int autoneg;
+
+ int link_timeout;
+
+ /* Interrupt number for this PHY
+ * -1 means no interrupt */
+ int irq;
+
+ /* private data pointer */
+ /* For use by PHYs to maintain extra state */
+ void *priv;
+
+ /* Interrupt and Polling infrastructure */
+ struct work_struct phy_queue;
+ struct timer_list phy_timer;
+
+ spinlock_t lock;
+
+ struct net_device *attached_dev;
+
+ void (*adjust_link)(struct net_device *dev);
+
+ void (*adjust_state)(struct net_device *dev);
+};
+#define to_phy_device(d) container_of(d, struct phy_device, dev)
+
+/* struct phy_driver: Driver structure for a particular PHY type
+ *
+ * phy_id: The result of reading the UID registers of this PHY
+ * type, and ANDing them with the phy_id_mask. This driver
+ * only works for PHYs with IDs which match this field
+ * name: The friendly name of this PHY type
+ * phy_id_mask: Defines the important bits of the phy_id
+ * features: A list of features (speed, duplex, etc) supported
+ * by this PHY
+ * flags: A bitfield defining certain other features this PHY
+ * supports (like interrupts)
+ *
+ * The drivers must implement config_aneg and read_status. All
+ * other functions are optional. Note that none of these
+ * functions should be called from interrupt time. The goal is
+ * for the bus read/write functions to be able to block when the
+ * bus transaction is happening, and be freed up by an interrupt
+ * (The MPC85xx has this ability, though it is not currently
+ * supported in the driver).
+ */
+struct phy_driver {
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+ u32 flags;
+
+ /* Called to initialize the PHY,
+ * including after a reset */
+ int (*config_init)(struct phy_device *phydev);
+
+ /* Called during discovery. Used to set
+ * up device-specific structures, if any */
+ int (*probe)(struct phy_device *phydev);
+
+ /* PHY Power Management */
+ int (*suspend)(struct phy_device *phydev);
+ int (*resume)(struct phy_device *phydev);
+
+ /* Configures the advertisement and resets
+ * autonegotiation if phydev->autoneg is on,
+ * forces the speed to the current settings in phydev
+ * if phydev->autoneg is off */
+ int (*config_aneg)(struct phy_device *phydev);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct phy_device *phydev);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct phy_device *phydev);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct phy_device *phydev);
+
+ /* Clears up any memory if needed */
+ void (*remove)(struct phy_device *phydev);
+
+ struct device_driver driver;
+};
+#define to_phy_driver(d) container_of(d, struct phy_driver, driver)
+
+int phy_read(struct phy_device *phydev, u16 regnum);
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+struct phy_device* get_phy_device(struct mii_bus *bus, uint addr);
+int phy_clear_interrupt(struct phy_device *phydev);
+int phy_config_interrupt(struct phy_device *phydev, u32 interrupts);
+struct phy_device * phy_attach(struct net_device *dev,
+ const char *phy_id, u32 flags);
+struct phy_device * phy_connect(struct net_device *dev, const char
*phy_id,
+ void (*handler)(struct net_device *), u32 flags);
+void phy_disconnect(struct phy_device *phydev);
+void phy_detach(struct phy_device *phydev);
+void phy_start(struct phy_device *phydev);
+void phy_stop(struct phy_device *phydev);
+int phy_start_aneg(struct phy_device *phydev);
+
+int mdiobus_register(struct mii_bus *bus);
+void mdiobus_unregister(struct mii_bus *bus);
+void phy_sanitize_settings(struct phy_device *phydev);
+int phy_stop_interrupts(struct phy_device *phydev);
+
+static inline int phy_read_status(struct phy_device *phydev) {
+ return phydev->drv->read_status(phydev);
+}
+
+int genphy_config_advert(struct phy_device *phydev);
+int genphy_setup_forced(struct phy_device *phydev);
+int genphy_restart_aneg(struct phy_device *phydev);
+int genphy_config_aneg(struct phy_device *phydev);
+int genphy_update_link(struct phy_device *phydev);
+int genphy_read_status(struct phy_device *phydev);
+void phy_driver_unregister(struct phy_driver *drv);
+int phy_driver_register(struct phy_driver *new_driver);
+void phy_prepare_link(struct phy_device *phydev,
+ void (*adjust_link)(struct net_device *));
+void phy_start_machine(struct phy_device *phydev,
+ void (*handler)(struct net_device *));
+void phy_stop_machine(struct phy_device *phydev);
+int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+int phy_mii_ioctl(struct phy_device *phydev,
+ struct mii_ioctl_data *mii_data, int cmd);
+int phy_start_interrupts(struct phy_device *phydev);
+void phy_print_status(struct phy_device *phydev);
+
+extern struct bus_type mdio_bus_type;
+extern struct phy_driver genphy_driver;
+#endif /* __PHY_H */
^ permalink raw reply
* Re: [2.6 patch] net/ipv4/: possible cleanups
From: David S. Miller @ 2005-07-25 19:42 UTC (permalink / raw)
To: bunk; +Cc: netdev, linux-kernel
In-Reply-To: <20050723220530.GO3160@stusta.de>
From: Adrian Bunk <bunk@stusta.de>
Date: Sun, 24 Jul 2005 00:05:30 +0200
> This patch contains the following possible cleanups:
> - make needlessly global code static
> - #if 0 the following unused global function:
> - xfrm4_state.c: xfrm4_state_fini
> - remove the following unneeded EXPORT_SYMBOL's:
> - ip_output.c: ip_finish_output
> - ip_output.c: sysctl_ip_default_ttl
> - fib_frontend.c: ip_dev_find
> - inetpeer.c: inet_peer_idlock
> - ip_options.c: ip_options_compile
> - ip_options.c: ip_options_undo
> - net/core/request_sock.c: sysctl_max_syn_backlog
>
> Signed-off-by: Adrian Bunk <bunk@stusta.de>
Queued up to my net-2.6.14 tree, thanks Adrian.
^ permalink raw reply
* Re: Netlink connector
From: Evgeniy Polyakov @ 2005-07-25 19:33 UTC (permalink / raw)
To: Eric Leblond
Cc: Patrick McHardy, Andrew Morton, Harald Welte, netdev,
netfilter-devel, linux-kernel
In-Reply-To: <1122302623.29940.20.camel@localhost.localdomain>
On Mon, Jul 25, 2005 at 04:43:43PM +0200, Eric Leblond (eleblond@inl.fr) wrote:
> Le lundi 25 juillet 2005 à 16:32 +0200, Patrick McHardy a écrit :
> > Evgeniy Polyakov wrote:
> > > On Mon, Jul 25, 2005 at 02:02:10AM -0400, James Morris (jmorris@redhat.com) wrote:
> > If I understand correctly it tries to workaround some netlink
> > limitations (limited number of netlink families and multicast groups)
> > by sending everything to userspace and demultiplexing it there.
> > Same in the other direction, an additional layer on top of netlink
> > does basically the same thing netlink already does. This looks like
> > a step in the wrong direction to me, netlink should instead be fixed
> > to support what is needed.
>
> I totally agree with you, it could be great to fix netlink to support
> multiple queue.
> I like to be able to use projects like snort-inline or nufw together.
> This will make Netfilter really stronger.
> Furthermore, there's a repetition of filtering capabilities with such a
> solution. Netfilter has to filter to send to netlink and this is the
> same with the queue dispatcher. I think this introduce too much
> complexity.
Netlink is transport protocol - no need to add complexity into it,
it must be as simple as possible and thus extensible.
Multiple queues and filtering should be created on different layer, like
it is done for TCP/IP and other protocols.
I'm not advertising, but connector is exactly the place where
it can be implemented.
> my 0.02$
>
> BR,
> --
> Éric Leblond, eleblond@inl.fr
> Téléphone : 01 44 89 46 40, Fax : 01 44 89 45 01
> INL, http://www.inl.fr
>
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Netlink connector
From: Evgeniy Polyakov @ 2005-07-25 19:28 UTC (permalink / raw)
To: Patrick McHardy
Cc: Andrew Morton, Harald Welte, netdev, netfilter-devel,
linux-kernel
In-Reply-To: <42E4F800.1010908@trash.net>
On Mon, Jul 25, 2005 at 04:32:32PM +0200, Patrick McHardy (kaber@trash.net) wrote:
> Evgeniy Polyakov wrote:
> >On Mon, Jul 25, 2005 at 02:02:10AM -0400, James Morris
> >(jmorris@redhat.com) wrote:
> >
> >>On Sun, 24 Jul 2005, David S. Miller wrote:
> >>
> >>>From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> >>>Date: Sat, 23 Jul 2005 13:14:55 +0400
> >>>
> >>>
> >>>>Andrew has no objection against connector and it lives in -mm
> >>>
> >>>A patch sitting in -mm has zero significance.
> >
> >That is why I'm asking netdev@ people again...
>
> If I understand correctly it tries to workaround some netlink
> limitations (limited number of netlink families and multicast groups)
> by sending everything to userspace and demultiplexing it there.
> Same in the other direction, an additional layer on top of netlink
> does basically the same thing netlink already does. This looks like
> a step in the wrong direction to me, netlink should instead be fixed
> to support what is needed.
Not only it.
The main _first_ idea was to simplify userspace mesasge handling as much
as possible.
In first releases I called it ioctl-ng - any module that want ot
communicate with userspace in the way ioctl does,
requires skb allocation/freeing/handling.
Does RTC driver writer need to know what is the difference between
shared and cloned skb? Should kernel user of such message bus
have to know about skb at all?
With char device I only need to register my callback - with kernel
connector it is the same, but allows to use the whole power of netlink,
especially without nice ioctl features like different pointer size
in userspace and kernelspace.
And number of free netlink sockets is _very_ small, especially
if allocate new one for simple notifications, which can be easily done
using connector.
No need to allocate skb, no need to know who are those monsters in
header and so on.
And netlink can be extended to support it - netlink is a transport
protocol, it should not care about higher layer message handling,
connector instead will deliver message to the end user in a very
convenient form.
P.S. I've removed netdev@redhat.com - please do not add subscribers-only
private mail lists.
--
Evgeniy Polyakov
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox