From 4c45b3bf981cbac8639cd444e9ee130db71f2871 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 May 2003 18:42:33 -0300 Subject: o ipx headers: Coding Style code reformatting --- include/linux/ipx.h | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipx.h b/include/linux/ipx.h index a6fa09060107..4f29c60964c4 100644 --- a/include/linux/ipx.h +++ b/include/linux/ipx.h @@ -5,8 +5,7 @@ #define IPX_NODE_LEN 6 #define IPX_MTU 576 -struct sockaddr_ipx -{ +struct sockaddr_ipx { sa_family_t sipx_family; __u16 sipx_port; __u32 sipx_network; @@ -16,9 +15,8 @@ struct sockaddr_ipx }; /* - * So we can fit the extra info for SIOCSIFADDR into the address nicely + * So we can fit the extra info for SIOCSIFADDR into the address nicely */ - #define sipx_special sipx_port #define sipx_action sipx_zero #define IPX_DLTITF 0 @@ -56,14 +54,13 @@ struct ipx_config_data { * OLD Route Definition for backward compatibility. */ -struct ipx_route_def -{ - __u32 ipx_network; - __u32 ipx_router_network; +struct ipx_route_def { + __u32 ipx_network; + __u32 ipx_router_network; #define IPX_ROUTE_NO_ROUTER 0 - unsigned char ipx_router_node[IPX_NODE_LEN]; - unsigned char ipx_device[16]; - unsigned short ipx_flags; + unsigned char ipx_router_node[IPX_NODE_LEN]; + unsigned char ipx_device[16]; + unsigned short ipx_flags; #define IPX_RT_SNAP 8 #define IPX_RT_8022 4 #define IPX_RT_BLUEBOOK 2 @@ -71,7 +68,7 @@ struct ipx_route_def }; #define SIOCAIPXITFCRT (SIOCPROTOPRIVATE) -#define SIOCAIPXPRISLT (SIOCPROTOPRIVATE+1) -#define SIOCIPXCFGDATA (SIOCPROTOPRIVATE+2) -#define SIOCIPXNCPCONN (SIOCPROTOPRIVATE+3) -#endif /* def _IPX_H_ */ +#define SIOCAIPXPRISLT (SIOCPROTOPRIVATE + 1) +#define SIOCIPXCFGDATA (SIOCPROTOPRIVATE + 2) +#define SIOCIPXNCPCONN (SIOCPROTOPRIVATE + 3) +#endif /* _IPX_H_ */ -- cgit v1.2.3 From 277110dca3410778a25cf301487a6a7376dc8c7d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 4 May 2003 18:57:30 -0300 Subject: o list.h: implement list_for_each_entry_safe --- include/linux/list.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/list.h b/include/linux/list.h index 11b8674c14ff..a6dea8afd99d 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -296,6 +296,19 @@ static inline void list_splice_init(struct list_head *list, pos = list_entry(pos->member.next, typeof(*pos), member), \ prefetch(pos->member.next)) +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + /** * list_for_each_rcu - iterate over an rcu-protected list * @pos: the &struct list_head to use as a loop counter. -- cgit v1.2.3 From 7f1648a622916edb3ca08fe67696757f0ee70171 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 5 May 2003 10:14:29 -0700 Subject: [IPSEC]: Fix SADB_EALG_{3,}DESCBC values. --- include/linux/pfkeyv2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h index b5efb37a1c2f..cf3a2f162239 100644 --- a/include/linux/pfkeyv2.h +++ b/include/linux/pfkeyv2.h @@ -275,8 +275,8 @@ struct sadb_x_nat_t_port { /* Encryption algorithms */ #define SADB_EALG_NONE 0 -#define SADB_EALG_DESCBC 1 -#define SADB_EALG_3DESCBC 2 +#define SADB_EALG_DESCBC 2 +#define SADB_EALG_3DESCBC 3 #define SADB_X_EALG_CASTCBC 6 #define SADB_X_EALG_BLOWFISHCBC 7 #define SADB_EALG_NULL 11 -- cgit v1.2.3 From acec7ca3b8b69a234f1b179411d9bc1fb3b16b3c Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 5 May 2003 10:52:42 -0700 Subject: [FS]: Add seq_release_private and proc_net_fops_create helpers. --- fs/seq_file.c | 10 ++++++++++ include/linux/proc_fs.h | 11 ++++++++++- include/linux/seq_file.h | 1 + kernel/ksyms.c | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/seq_file.c b/fs/seq_file.c index 653dd00d1125..98d7a01c14a4 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -338,3 +338,13 @@ int single_release(struct inode *inode, struct file *file) kfree(op); return res; } + +int seq_release_private(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + + kfree(seq->private); + seq->private = NULL; + return seq_release(inode, file); +} + diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 21616fbf2c6e..9f15eadedb46 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -163,6 +163,15 @@ static inline struct proc_dir_entry *proc_net_create(const char *name, return create_proc_info_entry(name,mode,proc_net,get_info); } +static inline struct proc_dir_entry *proc_net_fops_create(const char *name, + mode_t mode, struct file_operations *fops) +{ + struct proc_dir_entry *res = create_proc_entry(name, mode, proc_net); + if (res) + res->proc_fops = fops; + return res; +} + static inline void proc_net_remove(const char *name) { remove_proc_entry(name,proc_net); @@ -171,7 +180,7 @@ static inline void proc_net_remove(const char *name) #else #define proc_root_driver NULL - +#define proc_net_fops_create(name,mode,fops) do {} while(0) static inline struct proc_dir_entry *proc_net_create(const char *name, mode_t mode, get_info_t *get_info) {return NULL;} static inline void proc_net_remove(const char *name) {} diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index ab12d2608f53..724d51768098 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -60,5 +60,6 @@ int seq_printf(struct seq_file *, const char *, ...) int single_open(struct file *, int (*)(struct seq_file *, void *), void *); int single_release(struct inode *, struct file *); +int seq_release_private(struct inode *, struct file *); #endif #endif diff --git a/kernel/ksyms.c b/kernel/ksyms.c index c8ca8a9fa5e1..6e9dcc34fccf 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -540,6 +540,7 @@ EXPORT_SYMBOL(seq_read); EXPORT_SYMBOL(seq_lseek); EXPORT_SYMBOL(single_open); EXPORT_SYMBOL(single_release); +EXPORT_SYMBOL(seq_release_private); /* Program loader interfaces */ #ifdef CONFIG_MMU -- cgit v1.2.3 From 70d0a61c26c85df016ef436bc0d51af2c0afd5f0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 5 May 2003 11:04:26 -0700 Subject: [NETFILTER]: Make NAT code handle non-linear skbs. Makes the NAT code and all NAT helpers handle non-linear skbs. Main trick is to introduce skb_ip_make_writable which handles all the decloning, linearizing, etc. --- include/linux/netfilter_ipv4/ip_nat_core.h | 14 +- include/linux/netfilter_ipv4/ip_nat_helper.h | 13 +- include/linux/netfilter_ipv4/ip_nat_protocol.h | 9 +- net/ipv4/netfilter/ip_nat_core.c | 202 +++++++---- net/ipv4/netfilter/ip_nat_helper.c | 448 +++++++++++-------------- net/ipv4/netfilter/ip_nat_proto_icmp.c | 17 +- net/ipv4/netfilter/ip_nat_proto_tcp.c | 44 ++- net/ipv4/netfilter/ip_nat_proto_udp.c | 16 +- net/ipv4/netfilter/ip_nat_proto_unknown.c | 7 +- net/ipv4/netfilter/ip_nat_standalone.c | 34 +- net/ipv4/netfilter/ip_nat_tftp.c | 24 +- 11 files changed, 430 insertions(+), 398 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv4/ip_nat_core.h b/include/linux/netfilter_ipv4/ip_nat_core.h index e34e08331052..5f1a0bea8073 100644 --- a/include/linux/netfilter_ipv4/ip_nat_core.h +++ b/include/linux/netfilter_ipv4/ip_nat_core.h @@ -16,10 +16,10 @@ extern unsigned int do_bindings(struct ip_conntrack *ct, extern struct list_head protos; -extern unsigned int icmp_reply_translation(struct sk_buff *skb, - struct ip_conntrack *conntrack, - unsigned int hooknum, - int dir); +extern int icmp_reply_translation(struct sk_buff **pskb, + struct ip_conntrack *conntrack, + unsigned int hooknum, + int dir); extern void replace_in_hashes(struct ip_conntrack *conntrack, struct ip_nat_info *info); @@ -30,4 +30,10 @@ extern void place_in_hashes(struct ip_conntrack *conntrack, extern struct ip_nat_protocol ip_nat_protocol_tcp; extern struct ip_nat_protocol ip_nat_protocol_udp; extern struct ip_nat_protocol ip_nat_protocol_icmp; + +/* 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 /* _IP_NAT_CORE_H */ diff --git a/include/linux/netfilter_ipv4/ip_nat_helper.h b/include/linux/netfilter_ipv4/ip_nat_helper.h index 8e2996f76fed..633a9c37170d 100644 --- a/include/linux/netfilter_ipv4/ip_nat_helper.h +++ b/include/linux/netfilter_ipv4/ip_nat_helper.h @@ -43,22 +43,23 @@ extern struct list_head helpers; extern int ip_nat_helper_register(struct ip_nat_helper *me); extern void ip_nat_helper_unregister(struct ip_nat_helper *me); + +/* These return true or false. */ extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, unsigned int match_offset, unsigned int match_len, - char *rep_buffer, + const char *rep_buffer, unsigned int rep_len); extern int ip_nat_mangle_udp_packet(struct sk_buff **skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, unsigned int match_offset, unsigned int match_len, - char *rep_buffer, + const char *rep_buffer, unsigned int rep_len); -extern int ip_nat_seq_adjust(struct sk_buff *skb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo); -extern void ip_nat_delete_sack(struct sk_buff *skb); +extern int ip_nat_seq_adjust(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo); #endif diff --git a/include/linux/netfilter_ipv4/ip_nat_protocol.h b/include/linux/netfilter_ipv4/ip_nat_protocol.h index 42e2ebf33997..998ab2bd325c 100644 --- a/include/linux/netfilter_ipv4/ip_nat_protocol.h +++ b/include/linux/netfilter_ipv4/ip_nat_protocol.h @@ -18,10 +18,11 @@ struct ip_nat_protocol unsigned int protonum; /* Do a packet translation according to the ip_nat_proto_manip - * and manip type. */ - void (*manip_pkt)(struct iphdr *iph, size_t len, - const struct ip_conntrack_manip *manip, - enum ip_nat_manip_type maniptype); + * and manip type. Return true if succeeded. */ + int (*manip_pkt)(struct sk_buff **pskb, + unsigned int hdroff, + const struct ip_conntrack_manip *manip, + enum ip_nat_manip_type maniptype); /* Is the manipable part of the tuple between min and max incl? */ int (*in_range)(const struct ip_conntrack_tuple *tuple, diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index 5065e4349dd3..d803e2dbdc6c 100644 --- a/net/ipv4/netfilter/ip_nat_core.c +++ b/net/ipv4/netfilter/ip_nat_core.c @@ -13,6 +13,8 @@ #include #include #include /* For tcp_prot in getorigdst */ +#include +#include #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) @@ -698,14 +700,26 @@ void place_in_hashes(struct ip_conntrack *conntrack, list_prepend(&byipsproto[ipsprotohash], &info->byipsproto); } -static void -manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, +/* Returns true if succeeded. */ +static int +manip_pkt(u_int16_t proto, + struct sk_buff **pskb, + unsigned int iphdroff, const struct ip_conntrack_manip *manip, - enum ip_nat_manip_type maniptype, - __u32 *nfcache) + enum ip_nat_manip_type maniptype) { - *nfcache |= NFC_ALTERED; - find_nat_proto(proto)->manip_pkt(iph, len, manip, maniptype); + struct iphdr *iph; + + (*pskb)->nfcache |= NFC_ALTERED; + if (!skb_ip_make_writable(pskb, iphdroff+sizeof(iph))) + return 0; + + iph = (void *)(*pskb)->data + iphdroff; + + /* Manipulate protcol part. */ + if (!find_nat_proto(proto)->manip_pkt(pskb, iphdroff + iph->ihl*4, + manip, maniptype)) + return 0; if (maniptype == IP_NAT_MANIP_SRC) { iph->check = ip_nat_cheat_check(~iph->saddr, manip->ip, @@ -716,17 +730,7 @@ manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, iph->check); iph->daddr = manip->ip; } -#if 0 - if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) - DEBUGP("IP: checksum on packet bad.\n"); - - if (proto == IPPROTO_TCP) { - void *th = (u_int32_t *)iph + iph->ihl; - if (tcp_v4_check(th, len - 4*iph->ihl, iph->saddr, iph->daddr, - csum_partial((char *)th, len-4*iph->ihl, 0))) - DEBUGP("TCP: checksum on packet bad\n"); - } -#endif + return 1; } static inline int exp_for_packet(struct ip_conntrack_expect *exp, @@ -754,25 +758,13 @@ do_bindings(struct ip_conntrack *ct, unsigned int i; struct ip_nat_helper *helper; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - int is_tcp = (*pskb)->nh.iph->protocol == IPPROTO_TCP; + int proto = (*pskb)->nh.iph->protocol; /* Need nat lock to protect against modification, but neither conntrack (referenced) and helper (deleted with synchronize_bh()) can vanish. */ READ_LOCK(&ip_nat_lock); for (i = 0; i < info->num_manips; i++) { - /* raw socket (tcpdump) may have clone of incoming - skb: don't disturb it --RR */ - if (skb_cloned(*pskb) && !(*pskb)->sk) { - struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); - if (!nskb) { - READ_UNLOCK(&ip_nat_lock); - return NF_DROP; - } - kfree_skb(*pskb); - *pskb = nskb; - } - if (info->manips[i].direction == dir && info->manips[i].hooknum == hooknum) { DEBUGP("Mangling %p: %s to %u.%u.%u.%u %u\n", @@ -781,12 +773,12 @@ do_bindings(struct ip_conntrack *ct, ? "SRC" : "DST", NIPQUAD(info->manips[i].manip.ip), htons(info->manips[i].manip.u.all)); - manip_pkt((*pskb)->nh.iph->protocol, - (*pskb)->nh.iph, - (*pskb)->len, - &info->manips[i].manip, - info->manips[i].maniptype, - &(*pskb)->nfcache); + if (manip_pkt(proto, pskb, 0, + &info->manips[i].manip, + info->manips[i].maniptype) < 0) { + READ_UNLOCK(&ip_nat_lock); + return NF_DROP; + } } } helper = info->helper; @@ -839,12 +831,14 @@ do_bindings(struct ip_conntrack *ct, /* Adjust sequence number only once per packet * (helper is called at all hooks) */ - if (is_tcp && (hooknum == NF_IP_POST_ROUTING - || hooknum == NF_IP_LOCAL_IN)) { + if (proto == IPPROTO_TCP + && (hooknum == NF_IP_POST_ROUTING + || hooknum == NF_IP_LOCAL_IN)) { DEBUGP("ip_nat_core: adjusting sequence number\n"); /* future: put this in a l4-proto specific function, * and call this function here. */ - ip_nat_seq_adjust(*pskb, ct, ctinfo); + if (!ip_nat_seq_adjust(pskb, ct, ctinfo)) + ret = NF_DROP; } return ret; @@ -855,39 +849,51 @@ do_bindings(struct ip_conntrack *ct, /* not reached */ } -unsigned int -icmp_reply_translation(struct sk_buff *skb, +int +icmp_reply_translation(struct sk_buff **pskb, struct ip_conntrack *conntrack, unsigned int hooknum, int dir) { - struct iphdr *iph = skb->nh.iph; - struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); - struct iphdr *inner = (struct iphdr *)(hdr + 1); - size_t datalen = skb->len - ((void *)inner - (void *)iph); + struct { + struct icmphdr icmp; + struct iphdr ip; + } *inside; unsigned int i; struct ip_nat_info *info = &conntrack->nat.info; - IP_NF_ASSERT(skb->len >= iph->ihl*4 + sizeof(struct icmphdr)); + if (!skb_ip_make_writable(pskb,(*pskb)->nh.iph->ihl*4+sizeof(*inside))) + return 0; + inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; + + /* We're actually going to mangle it beyond trivial checksum + adjustment, so make sure the current checksum is correct. */ + if ((*pskb)->ip_summed != CHECKSUM_UNNECESSARY + && (u16)csum_fold(skb_checksum(*pskb, (*pskb)->nh.iph->ihl*4, + (*pskb)->len, 0))) + return 0; + /* Must be RELATED */ - IP_NF_ASSERT(skb->nfct - (struct ip_conntrack *)skb->nfct->master + IP_NF_ASSERT((*pskb)->nfct + - (struct ip_conntrack *)(*pskb)->nfct->master == IP_CT_RELATED - || skb->nfct - (struct ip_conntrack *)skb->nfct->master + || (*pskb)->nfct + - (struct ip_conntrack *)(*pskb)->nfct->master == IP_CT_RELATED+IP_CT_IS_REPLY); /* Redirects on non-null nats must be dropped, else they'll start talking to each other without our translation, and be confused... --RR */ - if (hdr->type == ICMP_REDIRECT) { + if (inside->icmp.type == ICMP_REDIRECT) { /* Don't care about races here. */ if (info->initialized != ((1 << IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST)) || info->num_manips != 0) - return NF_DROP; + return 0; } DEBUGP("icmp_reply_translation: translating error %p hook %u dir %s\n", - skb, hooknum, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); + *pskb, hooknum, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); /* Note: May not be from a NAT'd host, but probably safest to do translation always as if it came from the host itself (even though a "host unreachable" coming from the host @@ -918,11 +924,13 @@ icmp_reply_translation(struct sk_buff *skb, ? "DST" : "SRC", NIPQUAD(info->manips[i].manip.ip), ntohs(info->manips[i].manip.u.udp.port)); - manip_pkt(inner->protocol, inner, - skb->len - ((void *)inner - (void *)iph), - &info->manips[i].manip, - !info->manips[i].maniptype, - &skb->nfcache); + if (manip_pkt(inside->ip.protocol, pskb, + (*pskb)->nh.iph->ihl*4 + + sizeof(inside->icmp), + &info->manips[i].manip, + !info->manips[i].maniptype) < 0) + goto unlock_fail; + /* Outer packet needs to have IP header NATed like it's a reply. */ @@ -932,22 +940,82 @@ icmp_reply_translation(struct sk_buff *skb, info->manips[i].maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", NIPQUAD(info->manips[i].manip.ip)); - manip_pkt(0, iph, skb->len, - &info->manips[i].manip, - info->manips[i].maniptype, - &skb->nfcache); + if (manip_pkt(0, pskb, 0, + &info->manips[i].manip, + info->manips[i].maniptype) < 0) + goto unlock_fail; } } READ_UNLOCK(&ip_nat_lock); - /* Since we mangled inside ICMP packet, recalculate its - checksum from scratch. (Hence the handling of incorrect - checksums in conntrack, so we don't accidentally fix one.) */ - hdr->checksum = 0; - hdr->checksum = ip_compute_csum((unsigned char *)hdr, - sizeof(*hdr) + datalen); + inside->icmp.checksum = 0; + inside->icmp.checksum = csum_fold(skb_checksum(*pskb, + (*pskb)->nh.iph->ihl*4, + (*pskb)->len, 0)); + return 1; - return NF_ACCEPT; + unlock_fail: + READ_UNLOCK(&ip_nat_lock); + return 0; +} + +int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len) +{ + struct sk_buff *nskb; + unsigned int iplen; + + if (writable_len > (*pskb)->len) + return 0; + + /* Not exclusive use of packet? Must copy. */ + if (skb_shared(*pskb) || skb_cloned(*pskb)) + goto copy_skb; + + /* Alexey says IP hdr is always modifiable and linear, so ok. */ + if (writable_len <= (*pskb)->nh.iph->ihl*4) + return 1; + + iplen = writable_len - (*pskb)->nh.iph->ihl*4; + + /* DaveM says protocol headers are also modifiable. */ + switch ((*pskb)->nh.iph->protocol) { + case IPPROTO_TCP: { + struct tcphdr hdr; + if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, + &hdr, sizeof(hdr)) != 0) + goto copy_skb; + if (writable_len <= (*pskb)->nh.iph->ihl*4 + hdr.doff*4) + goto pull_skb; + goto copy_skb; + } + case IPPROTO_UDP: + if (writable_len<=(*pskb)->nh.iph->ihl*4+sizeof(struct udphdr)) + goto pull_skb; + goto copy_skb; + case IPPROTO_ICMP: + if (writable_len + <= (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr)) + goto pull_skb; + goto copy_skb; + /* Insert other cases here as desired */ + } + +copy_skb: + nskb = skb_copy(*pskb, GFP_ATOMIC); + if (!nskb) + return 0; + BUG_ON(skb_is_nonlinear(nskb)); + + /* Rest of kernel will get very unhappy if we pass it a + suddenly-orphaned skbuff */ + if ((*pskb)->sk) + skb_set_owner_w(nskb, (*pskb)->sk); + kfree_skb(*pskb); + *pskb = nskb; + return 1; + +pull_skb: + return pskb_may_pull(*pskb, writable_len); } int __init ip_nat_init(void) diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c index 95badf71f2ca..5bd46e0f9c61 100644 --- a/net/ipv4/netfilter/ip_nat_helper.c +++ b/net/ipv4/netfilter/ip_nat_helper.c @@ -46,14 +46,14 @@ #endif DECLARE_LOCK(ip_nat_seqofs_lock); - -static inline int -ip_nat_resize_packet(struct sk_buff **skb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - int new_size) + +/* Setup TCP sequence correction given this change at this sequence */ +static inline void +adjust_tcp_sequence(u32 seq, + int sizediff, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) { - struct iphdr *iph; int dir; struct ip_nat_seq *this_way, *other_way; @@ -65,52 +65,89 @@ ip_nat_resize_packet(struct sk_buff **skb, this_way = &ct->nat.info.seq[dir]; other_way = &ct->nat.info.seq[!dir]; - if (new_size > (*skb)->len + skb_tailroom(*skb)) { - struct sk_buff *newskb; - newskb = skb_copy_expand(*skb, skb_headroom(*skb), - new_size - (*skb)->len, - GFP_ATOMIC); + DEBUGP("ip_nat_resize_packet: Seq_offset before: "); + DUMP_OFFSET(this_way); - if (!newskb) { - printk("ip_nat_resize_packet: oom\n"); - return 0; - } else { - kfree_skb(*skb); - *skb = newskb; - } + LOCK_BH(&ip_nat_seqofs_lock); + + /* SYN adjust. If it's uninitialized, of this is after last + * correction, record it: we don't handle more than one + * adjustment in the window, but do deal with common case of a + * retransmit */ + if (this_way->offset_before == this_way->offset_after + || before(this_way->correction_pos, seq)) { + this_way->correction_pos = seq; + this_way->offset_before = this_way->offset_after; + this_way->offset_after += sizediff; } + UNLOCK_BH(&ip_nat_seqofs_lock); - iph = (*skb)->nh.iph; - if (iph->protocol == IPPROTO_TCP) { - struct tcphdr *tcph = (void *)iph + iph->ihl*4; - - DEBUGP("ip_nat_resize_packet: Seq_offset before: "); - DUMP_OFFSET(this_way); - - LOCK_BH(&ip_nat_seqofs_lock); - - /* SYN adjust. If it's uninitialized, of this is after last - * correction, record it: we don't handle more than one - * adjustment in the window, but do deal with common case of a - * retransmit */ - if (this_way->offset_before == this_way->offset_after - || before(this_way->correction_pos, ntohl(tcph->seq))) { - this_way->correction_pos = ntohl(tcph->seq); - this_way->offset_before = this_way->offset_after; - this_way->offset_after = (int32_t) - this_way->offset_before + new_size - - (*skb)->len; - } + DEBUGP("ip_nat_resize_packet: Seq_offset after: "); + DUMP_OFFSET(this_way); +} + +/* Frobs data inside this packet, which is linear. */ +static void mangle_contents(struct sk_buff *skb, + unsigned int dataoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len) +{ + unsigned char *data; + + BUG_ON(skb_is_nonlinear(skb)); + data = (unsigned char *)skb->nh.iph + dataoff; - UNLOCK_BH(&ip_nat_seqofs_lock); + /* move post-replacement */ + memmove(data + match_offset + rep_len, + data + match_offset + match_len, + skb->tail - (data + match_offset + match_len)); - DEBUGP("ip_nat_resize_packet: Seq_offset after: "); - DUMP_OFFSET(this_way); + /* insert data from buffer */ + memcpy(data + match_offset, rep_buffer, rep_len); + + /* update skb info */ + if (rep_len > match_len) { + DEBUGP("ip_nat_mangle_packet: Extending packet by " + "%u from %u bytes\n", rep_len - match_len, + skb->len); + skb_put(skb, rep_len - match_len); + } else { + DEBUGP("ip_nat_mangle_packet: Shrinking packet from " + "%u from %u bytes\n", match_len - rep_len, + skb->len); + __skb_trim(skb, skb->len + rep_len - match_len); } - - return 1; + + /* fix IP hdr checksum information */ + skb->nh.iph->tot_len = htons(skb->len); + ip_send_check(skb->nh.iph); + skb->csum = csum_partial(data, skb->len - dataoff, 0); } +/* Unusual, but possible case. */ +static int enlarge_skb(struct sk_buff **pskb, unsigned int extra) +{ + struct sk_buff *nskb; + + if ((*pskb)->len + extra > 65535) + return 0; + + nskb = skb_copy_expand(*pskb, skb_headroom(*pskb), extra, GFP_ATOMIC); + if (!nskb) + return 0; + + /* Transfer socket to new skb. */ + if ((*pskb)->sk) + skb_set_owner_w(nskb, (*pskb)->sk); +#ifdef CONFIG_NETFILTER_DEBUG + nskb->nf_debug = (*pskb)->nf_debug; +#endif + kfree_skb(*pskb); + *pskb = nskb; + return 1; +} /* Generic function for mangling variable-length address changes inside * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX @@ -121,91 +158,41 @@ ip_nat_resize_packet(struct sk_buff **skb, * * */ int -ip_nat_mangle_tcp_packet(struct sk_buff **skb, +ip_nat_mangle_tcp_packet(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, unsigned int match_offset, unsigned int match_len, - char *rep_buffer, + const char *rep_buffer, unsigned int rep_len) { - struct iphdr *iph = (*skb)->nh.iph; + struct iphdr *iph; struct tcphdr *tcph; - unsigned char *data; - u_int32_t tcplen, newlen, newtcplen; - - tcplen = (*skb)->len - iph->ihl*4; - newtcplen = tcplen - match_len + rep_len; - newlen = iph->ihl*4 + newtcplen; - if (newlen > 65535) { - if (net_ratelimit()) - printk("ip_nat_mangle_tcp_packet: nat'ed packet " - "exceeds maximum packet size\n"); + if (!skb_ip_make_writable(pskb, (*pskb)->len)) return 0; - } - if ((*skb)->len != newlen) { - if (!ip_nat_resize_packet(skb, ct, ctinfo, newlen)) { - printk("resize_packet failed!!\n"); - return 0; - } - } + if (rep_len > match_len + && rep_len - match_len > skb_tailroom(*pskb) + && !enlarge_skb(pskb, rep_len - match_len)) + return 0; - /* Alexey says: if a hook changes _data_ ... it can break - original packet sitting in tcp queue and this is fatal */ - if (skb_cloned(*skb)) { - struct sk_buff *nskb = skb_copy(*skb, GFP_ATOMIC); - if (!nskb) { - if (net_ratelimit()) - printk("Out of memory cloning TCP packet\n"); - return 0; - } - /* Rest of kernel will get very unhappy if we pass it - a suddenly-orphaned skbuff */ - if ((*skb)->sk) - skb_set_owner_w(nskb, (*skb)->sk); - kfree_skb(*skb); - *skb = nskb; - } + SKB_LINEAR_ASSERT(*pskb); - /* skb may be copied !! */ - iph = (*skb)->nh.iph; + iph = (*pskb)->nh.iph; tcph = (void *)iph + iph->ihl*4; - data = (void *)tcph + tcph->doff*4; - - if (rep_len != match_len) - /* move post-replacement */ - memmove(data + match_offset + rep_len, - data + match_offset + match_len, - (*skb)->tail - (data + match_offset + match_len)); - - /* insert data from buffer */ - memcpy(data + match_offset, rep_buffer, rep_len); - - /* update skb info */ - if (newlen > (*skb)->len) { - DEBUGP("ip_nat_mangle_tcp_packet: Extending packet by " - "%u to %u bytes\n", newlen - (*skb)->len, newlen); - skb_put(*skb, newlen - (*skb)->len); - } else { - DEBUGP("ip_nat_mangle_tcp_packet: Shrinking packet from " - "%u to %u bytes\n", (*skb)->len, newlen); - skb_trim(*skb, newlen); - } - /* fix checksum information */ - - iph->tot_len = htons(newlen); - (*skb)->csum = csum_partial((char *)tcph + tcph->doff*4, - newtcplen - tcph->doff*4, 0); + mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4, + match_offset, match_len, rep_buffer, rep_len); tcph->check = 0; - tcph->check = tcp_v4_check(tcph, newtcplen, iph->saddr, iph->daddr, + tcph->check = tcp_v4_check(tcph, (*pskb)->len - iph->ihl*4, + iph->saddr, iph->daddr, csum_partial((char *)tcph, tcph->doff*4, - (*skb)->csum)); - ip_send_check(iph); - + (*pskb)->csum)); + adjust_tcp_sequence(ntohl(tcph->seq), + (int)match_len - (int)rep_len, + ct, ctinfo); return 1; } @@ -220,219 +207,164 @@ ip_nat_mangle_tcp_packet(struct sk_buff **skb, * should be fairly easy to do. */ int -ip_nat_mangle_udp_packet(struct sk_buff **skb, +ip_nat_mangle_udp_packet(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, unsigned int match_offset, unsigned int match_len, - char *rep_buffer, + const char *rep_buffer, unsigned int rep_len) { - struct iphdr *iph = (*skb)->nh.iph; - struct udphdr *udph = (void *)iph + iph->ihl * 4; - unsigned char *data; - u_int32_t udplen, newlen, newudplen; - - udplen = (*skb)->len - iph->ihl*4; - newudplen = udplen - match_len + rep_len; - newlen = iph->ihl*4 + newudplen; + struct iphdr *iph; + struct udphdr *udph; + int need_csum = ((*pskb)->csum != 0); - if (newlen > 65535) { - if (net_ratelimit()) - printk("ip_nat_mangle_udp_packet: nat'ed packet " - "exceeds maximum packet size\n"); + if (!skb_ip_make_writable(pskb, (*pskb)->len)) return 0; - } - if ((*skb)->len != newlen) { - if (!ip_nat_resize_packet(skb, ct, ctinfo, newlen)) { - printk("resize_packet failed!!\n"); - return 0; - } - } - - /* Alexey says: if a hook changes _data_ ... it can break - original packet sitting in tcp queue and this is fatal */ - if (skb_cloned(*skb)) { - struct sk_buff *nskb = skb_copy(*skb, GFP_ATOMIC); - if (!nskb) { - if (net_ratelimit()) - printk("Out of memory cloning TCP packet\n"); - return 0; - } - /* Rest of kernel will get very unhappy if we pass it - a suddenly-orphaned skbuff */ - if ((*skb)->sk) - skb_set_owner_w(nskb, (*skb)->sk); - kfree_skb(*skb); - *skb = nskb; - } + if (rep_len > match_len + && rep_len - match_len > skb_tailroom(*pskb) + && !enlarge_skb(pskb, rep_len - match_len)) + return 0; - /* skb may be copied !! */ - iph = (*skb)->nh.iph; + iph = (*pskb)->nh.iph; udph = (void *)iph + iph->ihl*4; - data = (void *)udph + sizeof(struct udphdr); - - if (rep_len != match_len) - /* move post-replacement */ - memmove(data + match_offset + rep_len, - data + match_offset + match_len, - (*skb)->tail - (data + match_offset + match_len)); + mangle_contents(*pskb, iph->ihl*4 + sizeof(*udph), + match_offset, match_len, rep_buffer, rep_len); - /* insert data from buffer */ - memcpy(data + match_offset, rep_buffer, rep_len); - - /* update skb info */ - if (newlen > (*skb)->len) { - DEBUGP("ip_nat_mangle_udp_packet: Extending packet by " - "%u to %u bytes\n", newlen - (*skb)->len, newlen); - skb_put(*skb, newlen - (*skb)->len); - } else { - DEBUGP("ip_nat_mangle_udp_packet: Shrinking packet from " - "%u to %u bytes\n", (*skb)->len, newlen); - skb_trim(*skb, newlen); - } - - /* update the length of the UDP and IP packets to the new values*/ - udph->len = htons((*skb)->len - iph->ihl*4); - iph->tot_len = htons(newlen); + /* update the length of the UDP packet */ + udph->len = htons((*pskb)->len - iph->ihl*4); /* fix udp checksum if udp checksum was previously calculated */ - if ((*skb)->csum != 0) { - (*skb)->csum = csum_partial((char *)udph + - sizeof(struct udphdr), - newudplen - sizeof(struct udphdr), - 0); - + if (need_csum) { udph->check = 0; - udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, - newudplen, IPPROTO_UDP, - csum_partial((char *)udph, + udph->check + = csum_tcpudp_magic(iph->saddr, iph->daddr, + (*pskb)->len - iph->ihl*4, + IPPROTO_UDP, + csum_partial((char *)udph, sizeof(struct udphdr), - (*skb)->csum)); - } - - ip_send_check(iph); - + (*pskb)->csum)); + } else + (*pskb)->csum = 0; return 1; } /* Adjust one found SACK option including checksum correction */ static void -sack_adjust(struct tcphdr *tcph, - unsigned char *ptr, +sack_adjust(struct sk_buff *skb, + struct tcphdr *tcph, + unsigned int sackoff, + unsigned int sackend, struct ip_nat_seq *natseq) { - struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2); - int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3; - int i; - - for (i = 0; i < num_sacks; i++, sp++) { + while (sackoff < sackend) { + struct tcp_sack_block *sack; u_int32_t new_start_seq, new_end_seq; - if (after(ntohl(sp->start_seq) - natseq->offset_before, + sack = (void *)skb->data + sackoff; + if (after(ntohl(sack->start_seq) - natseq->offset_before, natseq->correction_pos)) - new_start_seq = ntohl(sp->start_seq) + new_start_seq = ntohl(sack->start_seq) - natseq->offset_after; else - new_start_seq = ntohl(sp->start_seq) + new_start_seq = ntohl(sack->start_seq) - natseq->offset_before; new_start_seq = htonl(new_start_seq); - if (after(ntohl(sp->end_seq) - natseq->offset_before, + if (after(ntohl(sack->end_seq) - natseq->offset_before, natseq->correction_pos)) - new_end_seq = ntohl(sp->end_seq) + new_end_seq = ntohl(sack->end_seq) - natseq->offset_after; else - new_end_seq = ntohl(sp->end_seq) + new_end_seq = ntohl(sack->end_seq) - natseq->offset_before; new_end_seq = htonl(new_end_seq); DEBUGP("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", - ntohl(sp->start_seq), new_start_seq, - ntohl(sp->end_seq), new_end_seq); + ntohl(sack->start_seq), new_start_seq, + ntohl(sack->end_seq), new_end_seq); tcph->check = - ip_nat_cheat_check(~sp->start_seq, new_start_seq, - ip_nat_cheat_check(~sp->end_seq, + ip_nat_cheat_check(~sack->start_seq, new_start_seq, + ip_nat_cheat_check(~sack->end_seq, new_end_seq, tcph->check)); - - sp->start_seq = new_start_seq; - sp->end_seq = new_end_seq; + sack->start_seq = new_start_seq; + sack->end_seq = new_end_seq; + sackoff += sizeof(*sack); } } - -/* TCP SACK sequence number adjustment, return 0 if sack found and adjusted */ -static inline int -ip_nat_sack_adjust(struct sk_buff *skb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) +/* TCP SACK sequence number adjustment */ +static inline unsigned int +ip_nat_sack_adjust(struct sk_buff **pskb, + struct tcphdr *tcph, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) { - struct iphdr *iph; - struct tcphdr *tcph; - unsigned char *ptr; - int length, dir, sack_adjusted = 0; + unsigned int dir, optoff, optend; - iph = skb->nh.iph; - tcph = (void *)iph + iph->ihl*4; - length = (tcph->doff*4)-sizeof(struct tcphdr); - ptr = (unsigned char *)(tcph+1); + 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)) + return 0; dir = CTINFO2DIR(ctinfo); - while (length > 0) { - int opcode = *ptr++; - int opsize; + while (optoff < optend) { + /* Usually: option, length. */ + unsigned char *op = (*pskb)->data + optoff; - switch (opcode) { + switch (op[0]) { case TCPOPT_EOL: - return !sack_adjusted; + return 1; case TCPOPT_NOP: - length--; + optoff++; continue; default: - opsize = *ptr++; - if (opsize > length) /* no partial opts */ - return !sack_adjusted; - if (opcode == TCPOPT_SACK) { - /* found SACK */ - if((opsize >= (TCPOLEN_SACK_BASE - +TCPOLEN_SACK_PERBLOCK)) && - !((opsize - TCPOLEN_SACK_BASE) - % TCPOLEN_SACK_PERBLOCK)) - sack_adjust(tcph, ptr-2, - &ct->nat.info.seq[!dir]); - - sack_adjusted = 1; - } - ptr += opsize-2; - length -= opsize; + /* no partial options */ + if (optoff + 1 == optend + || optoff + op[1] > optend + || op[1] < 2) + return 0; + if (op[0] == TCPOPT_SACK + && op[1] >= 2+TCPOLEN_SACK_PERBLOCK + && ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) + sack_adjust(*pskb, tcph, optoff+2, + optoff+op[1], + &ct->nat.info.seq[!dir]); + optoff += op[1]; } } - return !sack_adjusted; + return 1; } -/* TCP sequence number adjustment */ -int -ip_nat_seq_adjust(struct sk_buff *skb, +/* TCP sequence number adjustment. Returns true or false. */ +int +ip_nat_seq_adjust(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { - struct iphdr *iph; struct tcphdr *tcph; int dir, newseq, newack; struct ip_nat_seq *this_way, *other_way; - - iph = skb->nh.iph; - tcph = (void *)iph + iph->ihl*4; dir = CTINFO2DIR(ctinfo); this_way = &ct->nat.info.seq[dir]; other_way = &ct->nat.info.seq[!dir]; - + + /* No adjustments to make? Very common case. */ + if (!this_way->offset_before && !this_way->offset_after + && !other_way->offset_before && !other_way->offset_after) + return 1; + + if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph))) + return 0; + + tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; if (after(ntohl(tcph->seq), this_way->correction_pos)) newseq = ntohl(tcph->seq) + this_way->offset_after; else @@ -458,9 +390,7 @@ ip_nat_seq_adjust(struct sk_buff *skb, tcph->seq = newseq; tcph->ack_seq = newack; - ip_nat_sack_adjust(skb, ct, ctinfo); - - return 0; + return ip_nat_sack_adjust(pskb, tcph, ct, ctinfo); } static inline int diff --git a/net/ipv4/netfilter/ip_nat_proto_icmp.c b/net/ipv4/netfilter/ip_nat_proto_icmp.c index 9bc7427ce53f..ea507e2af9ba 100644 --- a/net/ipv4/netfilter/ip_nat_proto_icmp.c +++ b/net/ipv4/netfilter/ip_nat_proto_icmp.c @@ -42,17 +42,24 @@ icmp_unique_tuple(struct ip_conntrack_tuple *tuple, return 0; } -static void -icmp_manip_pkt(struct iphdr *iph, size_t len, +static int +icmp_manip_pkt(struct sk_buff **pskb, + unsigned int hdroff, const struct ip_conntrack_manip *manip, enum ip_nat_manip_type maniptype) { - struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); + struct icmphdr *hdr; + + if (!skb_ip_make_writable(pskb, hdroff + sizeof(*hdr))) + return 0; + + hdr = (void *)(*pskb)->data + hdroff; hdr->checksum = ip_nat_cheat_check(hdr->un.echo.id ^ 0xFFFF, - manip->u.icmp.id, - hdr->checksum); + manip->u.icmp.id, + hdr->checksum); hdr->un.echo.id = manip->u.icmp.id; + return 1; } static unsigned int diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c index d27959ea3c41..73a25130e797 100644 --- a/net/ipv4/netfilter/ip_nat_proto_tcp.c +++ b/net/ipv4/netfilter/ip_nat_proto_tcp.c @@ -7,6 +7,7 @@ #include #include #include +#include static int tcp_in_range(const struct ip_conntrack_tuple *tuple, @@ -73,36 +74,49 @@ tcp_unique_tuple(struct ip_conntrack_tuple *tuple, return 0; } -static void -tcp_manip_pkt(struct iphdr *iph, size_t len, +static int +tcp_manip_pkt(struct sk_buff **pskb, + unsigned int hdroff, const struct ip_conntrack_manip *manip, enum ip_nat_manip_type maniptype) { - struct tcphdr *hdr = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + struct tcphdr *hdr; u_int32_t oldip; - u_int16_t *portptr; + u_int16_t *portptr, oldport; + int hdrsize = 8; /* TCP connection tracking guarantees this much */ + + /* this could be a inner header returned in icmp packet; in such + cases we cannot update the checksum field since it is outside of + the 8 bytes of transport layer headers we are guaranteed */ + if ((*pskb)->len >= hdroff + sizeof(struct tcphdr)) + hdrsize = sizeof(struct tcphdr); + + if (!skb_ip_make_writable(pskb, hdroff + hdrsize)) + return 0; + + hdr = (void *)(*pskb)->data + hdroff; if (maniptype == IP_NAT_MANIP_SRC) { /* Get rid of src ip and src pt */ - oldip = iph->saddr; + oldip = (*pskb)->nh.iph->saddr; portptr = &hdr->source; } else { /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; + oldip = (*pskb)->nh.iph->daddr; portptr = &hdr->dest; } - /* this could be a inner header returned in icmp packet; in such - cases we cannot update the checksum field since it is outside of - the 8 bytes of transport layer headers we are guaranteed */ - if(((void *)&hdr->check + sizeof(hdr->check) - (void *)iph) <= len) { - hdr->check = ip_nat_cheat_check(~oldip, manip->ip, - ip_nat_cheat_check(*portptr ^ 0xFFFF, + oldport = *portptr; + *portptr = manip->u.tcp.port; + + if (hdrsize < sizeof(*hdr)) + return 1; + + hdr->check = ip_nat_cheat_check(~oldip, manip->ip, + ip_nat_cheat_check(oldport ^ 0xFFFF, manip->u.tcp.port, hdr->check)); - } - - *portptr = manip->u.tcp.port; + return 1; } static unsigned int diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c index 622aee05a04c..97a04ec06673 100644 --- a/net/ipv4/netfilter/ip_nat_proto_udp.c +++ b/net/ipv4/netfilter/ip_nat_proto_udp.c @@ -72,22 +72,27 @@ udp_unique_tuple(struct ip_conntrack_tuple *tuple, return 0; } -static void -udp_manip_pkt(struct iphdr *iph, size_t len, +static int +udp_manip_pkt(struct sk_buff **pskb, + unsigned int hdroff, const struct ip_conntrack_manip *manip, enum ip_nat_manip_type maniptype) { - struct udphdr *hdr = (struct udphdr *)((u_int32_t *)iph + iph->ihl); + struct udphdr *hdr; u_int32_t oldip; u_int16_t *portptr; + if (!skb_ip_make_writable(pskb, hdroff + sizeof(hdr))) + return 0; + + hdr = (void *)(*pskb)->data + hdroff; if (maniptype == IP_NAT_MANIP_SRC) { /* Get rid of src ip and src pt */ - oldip = iph->saddr; + oldip = (*pskb)->nh.iph->saddr; portptr = &hdr->source; } else { /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; + oldip = (*pskb)->nh.iph->daddr; portptr = &hdr->dest; } if (hdr->check) /* 0 is a special case meaning no checksum */ @@ -96,6 +101,7 @@ udp_manip_pkt(struct iphdr *iph, size_t len, manip->u.udp.port, hdr->check)); *portptr = manip->u.udp.port; + return 1; } static unsigned int diff --git a/net/ipv4/netfilter/ip_nat_proto_unknown.c b/net/ipv4/netfilter/ip_nat_proto_unknown.c index a2b5de683dce..e918e464b222 100644 --- a/net/ipv4/netfilter/ip_nat_proto_unknown.c +++ b/net/ipv4/netfilter/ip_nat_proto_unknown.c @@ -29,12 +29,13 @@ static int unknown_unique_tuple(struct ip_conntrack_tuple *tuple, return 0; } -static void -unknown_manip_pkt(struct iphdr *iph, size_t len, +static int +unknown_manip_pkt(struct sk_buff **pskb, + unsigned int hdroff, const struct ip_conntrack_manip *manip, enum ip_nat_manip_type maniptype) { - return; + return 1; } static unsigned int diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c index 69cf5c6ae27a..9a97186c72c2 100644 --- a/net/ipv4/netfilter/ip_nat_standalone.c +++ b/net/ipv4/netfilter/ip_nat_standalone.c @@ -71,10 +71,6 @@ ip_nat_fn(unsigned int hooknum, /* maniptype == SRC for postrouting. */ enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum); - /* FIXME: Push down to extensions --RR */ - if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0) - return NF_DROP; - /* We never see fragments: conntrack defrags on pre-routing and local-out, and ip_nat_out protects post-routing. */ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off @@ -95,12 +91,14 @@ ip_nat_fn(unsigned int hooknum, /* Exception: ICMP redirect to new connection (not in hash table yet). We must not let this through, in case we're doing NAT to the same network. */ - struct iphdr *iph = (*pskb)->nh.iph; - struct icmphdr *hdr = (struct icmphdr *) - ((u_int32_t *)iph + iph->ihl); - if (iph->protocol == IPPROTO_ICMP - && hdr->type == ICMP_REDIRECT) - return NF_DROP; + if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { + struct icmphdr hdr; + + if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, + &hdr, sizeof(hdr)) == 0 + && hdr.type == ICMP_REDIRECT) + return NF_DROP; + } return NF_ACCEPT; } @@ -108,8 +106,11 @@ ip_nat_fn(unsigned int hooknum, case IP_CT_RELATED: case IP_CT_RELATED+IP_CT_IS_REPLY: if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { - return icmp_reply_translation(*pskb, ct, hooknum, - CTINFO2DIR(ctinfo)); + if (!icmp_reply_translation(pskb, ct, hooknum, + CTINFO2DIR(ctinfo))) + return NF_DROP; + else + return NF_ACCEPT; } /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ case IP_CT_NEW: @@ -174,10 +175,6 @@ ip_nat_out(unsigned int hooknum, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - /* FIXME: Push down to extensions --RR */ - if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0) - return NF_DROP; - /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) @@ -213,10 +210,6 @@ ip_nat_local_fn(unsigned int hooknum, u_int32_t saddr, daddr; unsigned int ret; - /* FIXME: Push down to extensions --RR */ - if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0) - return NF_DROP; - /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) @@ -387,4 +380,5 @@ EXPORT_SYMBOL(ip_nat_cheat_check); EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); EXPORT_SYMBOL(ip_nat_mangle_udp_packet); EXPORT_SYMBOL(ip_nat_used_tuple); +EXPORT_SYMBOL(skb_ip_make_writable); MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/ip_nat_tftp.c b/net/ipv4/netfilter/ip_nat_tftp.c index bce0f40fd974..4bfe1e8bfb40 100644 --- a/net/ipv4/netfilter/ip_nat_tftp.c +++ b/net/ipv4/netfilter/ip_nat_tftp.c @@ -57,9 +57,7 @@ tftp_nat_help(struct ip_conntrack *ct, struct sk_buff **pskb) { int dir = CTINFO2DIR(ctinfo); - struct iphdr *iph = (*pskb)->nh.iph; - struct udphdr *udph = (void *)iph + iph->ihl * 4; - struct tftphdr *tftph = (void *)udph + 8; + struct tftphdr tftph; struct ip_conntrack_tuple repl; if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) @@ -71,7 +69,11 @@ tftp_nat_help(struct ip_conntrack *ct, return NF_ACCEPT; } - switch (ntohs(tftph->opcode)) { + if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4+sizeof(struct udphdr), + &tftph, sizeof(tftph)) != 0) + return NF_DROP; + + switch (ntohs(tftph.opcode)) { /* RRQ and WRQ works the same way */ case TFTP_OPCODE_READ: case TFTP_OPCODE_WRITE: @@ -104,8 +106,10 @@ tftp_nat_expected(struct sk_buff **pskb, #if 0 const struct ip_conntrack_tuple *repl = &master->tuplehash[IP_CT_DIR_REPLY].tuple; - struct iphdr *iph = (*pskb)->nh.iph; - struct udphdr *udph = (void *)iph + iph->ihl*4; + struct udphdr udph; + + if (skb_copy_bits(*pskb,(*pskb)->nh.iph->ihl*4,&udph,sizeof(udph))!=0) + return NF_DROP; #endif IP_NF_ASSERT(info); @@ -119,8 +123,8 @@ tftp_nat_expected(struct sk_buff **pskb, mr.range[0].min_ip = mr.range[0].max_ip = orig->dst.ip; DEBUGP("orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " "newsrc: %u.%u.%u.%u\n", - NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), - NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph.source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph.dest), NIPQUAD(orig->dst.ip)); } else { mr.range[0].min_ip = mr.range[0].max_ip = orig->src.ip; @@ -130,8 +134,8 @@ tftp_nat_expected(struct sk_buff **pskb, DEBUGP("orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " "newdst: %u.%u.%u.%u:%u\n", - NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), - NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph.source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph.dest), NIPQUAD(orig->src.ip), ntohs(orig->src.u.udp.port)); } -- cgit v1.2.3 From 22f0b1c04d049a47efcbdf387ad706d4786d78d8 Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Tue, 6 May 2003 03:16:15 -0700 Subject: [PATCH] i2c #1/3: listify i2c core This is the first of tree patches for i2c. Trying to get the i2c cleanups finshed before 2.6.x, so we (hopefully) don't have a ever-changing i2c subsystem in 2.7.x again (which is very annonying for driver maintainance). Changes: * listify i2c-core, i.e. make it use instead of statically-sized arrays, removed lots of ugly code :) * added i2c_(get|put)_adapter, changed i2c-dev.c to use these functions instead maintaining is own adapter list. * killed the I2C_DF_DUMMY flag which had the strange semantics to make the i2c subsystem call driver->attach_adapter on detaches. Added a detach_adapter() callback instead. * some other minor cleanups along the way ... --- drivers/i2c/i2c-core.c | 325 ++++++++++++++++++------------------------ drivers/i2c/i2c-dev.c | 62 ++++---- drivers/media/video/dpc7146.c | 13 +- drivers/media/video/mxb.c | 33 +++-- drivers/media/video/tvmixer.c | 17 ++- include/linux/i2c.h | 28 ++-- 6 files changed, 213 insertions(+), 265 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index d3d8c0e925f6..690060c4aa8b 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -38,8 +38,8 @@ #define DEB(x) if (i2c_debug>=1) x; #define DEB2(x) if (i2c_debug>=2) x; -static struct i2c_adapter *adapters[I2C_ADAP_MAX]; -static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static LIST_HEAD(adapters); +static LIST_HEAD(drivers); static DECLARE_MUTEX(core_lists); /**** debug level */ @@ -75,23 +75,17 @@ static struct device_driver i2c_generic_driver = { */ int i2c_add_adapter(struct i2c_adapter *adap) { - int res = 0, i, j; + static int nr = 0; + struct list_head *item; + struct i2c_driver *driver; down(&core_lists); - for (i = 0; i < I2C_ADAP_MAX; i++) - if (NULL == adapters[i]) - break; - if (I2C_ADAP_MAX == i) { - dev_warn(&adap->dev, - "register_adapter - enlarge I2C_ADAP_MAX.\n"); - res = -ENOMEM; - goto out_unlock; - } - adapters[i] = adap; - - init_MUTEX(&adap->bus); - init_MUTEX(&adap->list); + adap->nr = nr++; + init_MUTEX(&adap->bus_lock); + init_MUTEX(&adap->clist_lock); + list_add_tail(&adap->list,&adapters); + INIT_LIST_HEAD(&adap->clients); /* Add the adapter to the driver core. * If the parent pointer is not set up, @@ -99,77 +93,65 @@ int i2c_add_adapter(struct i2c_adapter *adap) */ if (adap->dev.parent == NULL) adap->dev.parent = &legacy_bus; - sprintf(adap->dev.bus_id, "i2c-%d", i); + sprintf(adap->dev.bus_id, "i2c-%d", adap->nr); adap->dev.driver = &i2c_generic_driver; device_register(&adap->dev); /* inform drivers of new adapters */ - for (j=0;jflags&(I2C_DF_NOTIFY|I2C_DF_DUMMY))) + list_for_each(item,&drivers) { + driver = list_entry(item, struct i2c_driver, list); + if (driver->flags & I2C_DF_NOTIFY) /* We ignore the return code; if it fails, too bad */ - drivers[j]->attach_adapter(adap); + driver->attach_adapter(adap); + } up(&core_lists); - - DEB(dev_dbg(&adap->dev, "registered as adapter %d.\n", i)); - out_unlock: - up(&core_lists); - return res;; + DEB(dev_dbg(&adap->dev, "registered as adapter #%d\n", adap->nr)); + return 0; } int i2c_del_adapter(struct i2c_adapter *adap) { - int res = 0, i, j; + struct list_head *item; + struct i2c_driver *driver; + struct i2c_client *client; + int res = 0; down(&core_lists); - for (i = 0; i < I2C_ADAP_MAX; i++) - if (adap == adapters[i]) - break; - if (I2C_ADAP_MAX == i) { - dev_warn(&adap->dev, "unregister_adapter adap not found.\n"); - res = -ENODEV; - goto out_unlock; - } - /* DUMMY drivers do not register their clients, so we have to - * use a trick here: we call driver->attach_adapter to - * *detach* it! Of course, each dummy driver should know about - * this or hell will break loose... - */ - for (j = 0; j < I2C_DRIVER_MAX; j++) - if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY)) - if ((res = drivers[j]->attach_adapter(adap))) { + list_for_each(item,&drivers) { + driver = list_entry(item, struct i2c_driver, list); + if (driver->detach_adapter) + if ((res = driver->detach_adapter(adap))) { dev_warn(&adap->dev, "can't detach adapter" - "while detaching driver %s: driver not " - "detached!", drivers[j]->name); + "while detaching driver %s: driver not " + "detached!", driver->name); goto out_unlock; } + } /* detach any active clients. This must be done first, because * it can fail; in which case we give upp. */ - for (j=0;jclients[j]; - if (client!=NULL) { - /* detaching devices is unconditional of the set notify - * flag, as _all_ clients that reside on the adapter - * must be deleted, as this would cause invalid states. - */ - if ((res=client->driver->detach_client(client))) { - dev_err(&adap->dev, "adapter not " - "unregistered, because client at " - "address %02x can't be detached. ", - client->addr); - goto out_unlock; - } + list_for_each(item,&adap->clients) { + client = list_entry(item, struct i2c_client, list); + + /* detaching devices is unconditional of the set notify + * flag, as _all_ clients that reside on the adapter + * must be deleted, as this would cause invalid states. + */ + if ((res=client->driver->detach_client(client))) { + dev_err(&adap->dev, "adapter not " + "unregistered, because client at " + "address %02x can't be detached. ", + client->addr); + goto out_unlock; } } /* clean up the sysfs representation */ device_unregister(&adap->dev); - - adapters[i] = NULL; + list_del(&adap->list); DEB(dev_dbg(&adap->dev, "adapter unregistered\n")); @@ -187,24 +169,11 @@ int i2c_del_adapter(struct i2c_adapter *adap) int i2c_add_driver(struct i2c_driver *driver) { - int res = 0, i; + struct list_head *item; + struct i2c_adapter *adapter; + int res = 0; down(&core_lists); - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (NULL == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) { - printk(KERN_WARNING - " i2c-core.o: register_driver(%s) " - "- enlarge I2C_DRIVER_MAX.\n", - driver->name); - res = -ENOMEM; - goto out_unlock; - } - - drivers[i] = driver; - - DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name)); /* add the driver to the list of i2c drivers in the driver core */ driver->driver.name = driver->name; @@ -216,13 +185,14 @@ int i2c_add_driver(struct i2c_driver *driver) if (res) goto out_unlock; - /* now look for instances of driver on our adapters - */ - if (driver->flags& (I2C_DF_NOTIFY|I2C_DF_DUMMY)) { - for (i=0;iattach_adapter(adapters[i]); + list_add_tail(&driver->list,&drivers); + DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name)); + + /* now look for instances of driver on our adapters */ + if (driver->flags & I2C_DF_NOTIFY) { + list_for_each(item,&adapters) { + adapter = list_entry(item, struct i2c_adapter, list); + driver->attach_adapter(adapter); } } @@ -233,44 +203,29 @@ int i2c_add_driver(struct i2c_driver *driver) int i2c_del_driver(struct i2c_driver *driver) { - int res = 0, i, j, k; + struct list_head *item1; + struct list_head *item2; + struct i2c_client *client; + struct i2c_adapter *adap; + + int res = 0; down(&core_lists); - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (driver == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) { - printk(KERN_WARNING " i2c-core.o: unregister_driver: " - "[%s] not found\n", - driver->name); - res = -ENODEV; - goto out_unlock; - } - - driver_unregister(&driver->driver); /* Have a look at each adapter, if clients of this driver are still * attached. If so, detach them to be able to kill the driver * afterwards. */ DEB2(printk(KERN_DEBUG "i2c-core.o: unregister_driver - looking for clients.\n")); - /* removing clients does not depend on the notify flag, else * invalid operation might (will!) result, when using stale client * pointers. */ - for (k=0;kdev, "examining adapter\n")); - if (driver->flags & I2C_DF_DUMMY) { - /* DUMMY drivers do not register their clients, so we have to - * use a trick here: we call driver->attach_adapter to - * *detach* it! Of course, each dummy driver should know about - * this or hell will break loose... - */ - if ((res = driver->attach_adapter(adap))) { + if (driver->detach_adapter) { + if ((res = driver->detach_adapter(adap))) { dev_warn(&adap->dev, "while unregistering " "dummy driver %s, adapter could " "not be detached properly; driver " @@ -278,31 +233,31 @@ int i2c_del_driver(struct i2c_driver *driver) goto out_unlock; } } else { - for (j=0;jclients[j]; - if (client != NULL && - client->driver == driver) { - DEB2(printk(KERN_DEBUG "i2c-core.o: " - "detaching client %s:\n", - client->dev.name)); - if ((res = driver->detach_client(client))) { - dev_err(&adap->dev, "while " - "unregistering driver " - "`%s', the client at " - "address %02x of " - "adapter could not " - "be detached; driver " - "not unloaded!", - driver->name, - client->addr); - goto out_unlock; - } + list_for_each(item2,&adap->clients) { + client = list_entry(item2, struct i2c_client, list); + if (client->driver != driver) + continue; + DEB2(printk(KERN_DEBUG "i2c-core.o: " + "detaching client %s:\n", + client->dev.name)); + if ((res = driver->detach_client(client))) { + dev_err(&adap->dev, "while " + "unregistering driver " + "`%s', the client at " + "address %02x of " + "adapter could not " + "be detached; driver " + "not unloaded!", + driver->name, + client->addr); + goto out_unlock; } } } } - drivers[i] = NULL; - + + driver_unregister(&driver->driver); + list_del(&driver->list); DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name)); out_unlock: @@ -310,14 +265,16 @@ int i2c_del_driver(struct i2c_driver *driver) return 0; } -static int __i2c_check_addr(struct i2c_adapter *adapter, int addr) +static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr) { - int i; + struct list_head *item; + struct i2c_client *client; - for (i = 0; i < I2C_CLIENT_MAX ; i++) - if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + list_for_each(item,&adapter->clients) { + client = list_entry(item, struct i2c_client, list); + if (client->addr == addr) return -EBUSY; - + } return 0; } @@ -325,9 +282,9 @@ int i2c_check_addr(struct i2c_adapter *adapter, int addr) { int rval; - down(&adapter->list); + down(&adapter->clist_lock); rval = __i2c_check_addr(adapter, addr); - up(&adapter->list); + up(&adapter->clist_lock); return rval; } @@ -335,28 +292,14 @@ int i2c_check_addr(struct i2c_adapter *adapter, int addr) int i2c_attach_client(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; - int i; - - down(&adapter->list); - if (__i2c_check_addr(client->adapter, client->addr)) - goto out_unlock_list; - for (i = 0; i < I2C_CLIENT_MAX; i++) { - if (!adapter->clients[i]) - goto free_slot; + down(&adapter->clist_lock); + if (__i2c_check_addr(client->adapter, client->addr)) { + up(&adapter->clist_lock); + return -EBUSY; } - - printk(KERN_WARNING - " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", - client->dev.name); - - out_unlock_list: - up(&adapter->list); - return -EBUSY; - - free_slot: - adapter->clients[i] = client; - up(&adapter->list); + list_add_tail(&client->list,&adapter->clients); + up(&adapter->clist_lock); if (adapter->client_register) { if (adapter->client_register(client)) { @@ -388,7 +331,7 @@ int i2c_attach_client(struct i2c_client *client) int i2c_detach_client(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; - int res = 0, i; + int res = 0; if ((client->flags & I2C_CLIENT_ALLOW_USE) && (client->usage_count > 0)) return -EBUSY; @@ -403,22 +346,11 @@ int i2c_detach_client(struct i2c_client *client) } } - down(&adapter->list); - for (i = 0; i < I2C_CLIENT_MAX; i++) { - if (client == adapter->clients[i]) { - adapter->clients[i] = NULL; - goto out_unlock; - } - } - - printk(KERN_WARNING - " i2c-core.o: unregister_client [%s] not found\n", - client->dev.name); - res = -ENODEV; - - out_unlock: + down(&adapter->clist_lock); + list_del(&client->list); device_unregister(&client->dev); - up(&adapter->list); + up(&adapter->clist_lock); + out: return res; } @@ -516,9 +448,9 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) if (adap->algo->master_xfer) { DEB2(dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", num)); - down(&adap->bus); + down(&adap->bus_lock); ret = adap->algo->master_xfer(adap,msgs,num); - up(&adap->bus); + up(&adap->bus_lock); return ret; } else { @@ -542,9 +474,9 @@ int i2c_master_send(struct i2c_client *client,const char *buf ,int count) DEB2(dev_dbg(&client->adapter->dev, "master_send: writing %d bytes.\n", count)); - down(&adap->bus); + down(&adap->bus_lock); ret = adap->algo->master_xfer(adap,&msg,1); - up(&adap->bus); + up(&adap->bus_lock); /* if everything went ok (i.e. 1 msg transmitted), return #bytes * transmitted, else error code. @@ -572,9 +504,9 @@ int i2c_master_recv(struct i2c_client *client, char *buf ,int count) DEB2(dev_dbg(&client->adapter->dev, "master_recv: reading %d bytes.\n", count)); - down(&adap->bus); + down(&adap->bus_lock); ret = adap->algo->master_xfer(adap,&msg,1); - up(&adap->bus); + up(&adap->bus_lock); DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", ret, count, client->addr)); @@ -743,11 +675,30 @@ int i2c_probe(struct i2c_adapter *adapter, */ int i2c_adapter_id(struct i2c_adapter *adap) { - int i; - for (i = 0; i < I2C_ADAP_MAX; i++) - if (adap == adapters[i]) - return i; - return -1; + return adap->nr; +} + +struct i2c_adapter* i2c_get_adapter(int id) +{ + struct list_head *item; + struct i2c_adapter *adapter; + + down(&core_lists); + list_for_each(item,&adapters) { + adapter = list_entry(item, struct i2c_adapter, list); + if (id == adapter->nr && + try_module_get(adapter->owner)) { + up(&core_lists); + return adapter; + } + } + up(&core_lists); + return NULL; +} + +void i2c_put_adapter(struct i2c_adapter *adap) +{ + module_put(adap->owner); } /* The SMBus parts */ @@ -1189,10 +1140,10 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, } if (adapter->algo->smbus_xfer) { - down(&adapter->bus); + down(&adapter->bus_lock); res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, command,size,data); - up(&adapter->bus); + up(&adapter->bus_lock); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, command,size,data); @@ -1239,6 +1190,8 @@ EXPORT_SYMBOL(i2c_master_recv); EXPORT_SYMBOL(i2c_control); EXPORT_SYMBOL(i2c_transfer); EXPORT_SYMBOL(i2c_adapter_id); +EXPORT_SYMBOL(i2c_get_adapter); +EXPORT_SYMBOL(i2c_put_adapter); EXPORT_SYMBOL(i2c_probe); EXPORT_SYMBOL(i2c_smbus_xfer); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index ac939bd90b5d..a7aba262f11a 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -58,6 +58,7 @@ static int i2cdev_open (struct inode *inode, struct file *file); static int i2cdev_release (struct inode *inode, struct file *file); static int i2cdev_attach_adapter(struct i2c_adapter *adap); +static int i2cdev_detach_adapter(struct i2c_adapter *adap); static int i2cdev_detach_client(struct i2c_client *client); static int i2cdev_command(struct i2c_client *client, unsigned int cmd, void *arg); @@ -72,15 +73,13 @@ static struct file_operations i2cdev_fops = { .release = i2cdev_release, }; -#define I2CDEV_ADAPS_MAX I2C_ADAP_MAX -static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX]; - static struct i2c_driver i2cdev_driver = { .owner = THIS_MODULE, .name = "dev driver", .id = I2C_DRIVERID_I2CDEV, - .flags = I2C_DF_DUMMY, + .flags = I2C_DF_NOTIFY, .attach_adapter = i2cdev_attach_adapter, + .detach_adapter = i2cdev_detach_adapter, .detach_client = i2cdev_detach_client, .command = i2cdev_command, }; @@ -340,35 +339,31 @@ static int i2cdev_open(struct inode *inode, struct file *file) { unsigned int minor = minor(inode->i_rdev); struct i2c_client *client; + struct i2c_adapter *adap; - if ((minor >= I2CDEV_ADAPS_MAX) || !(i2cdev_adaps[minor])) + adap = i2c_get_adapter(minor); + if (NULL == adap) return -ENODEV; client = kmalloc(sizeof(*client), GFP_KERNEL); - if (!client) + if (!client) { + i2c_put_adapter(adap); return -ENOMEM; + } memcpy(client, &i2cdev_client_template, sizeof(*client)); /* registered with adapter, passed as client to user */ - client->adapter = i2cdev_adaps[minor]; + client->adapter = adap; file->private_data = client; - /* use adapter module, i2c-dev handled with fops */ - if (!try_module_get(client->adapter->owner)) - goto out_kfree; - return 0; - -out_kfree: - kfree(client); - return -ENODEV; } static int i2cdev_release(struct inode *inode, struct file *file) { struct i2c_client *client = file->private_data; - module_put(client->adapter->owner); + i2c_put_adapter(client->adapter); kfree(client); file->private_data = NULL; @@ -377,33 +372,28 @@ static int i2cdev_release(struct inode *inode, struct file *file) int i2cdev_attach_adapter(struct i2c_adapter *adap) { - int i; char name[12]; + int i; - if ((i = i2c_adapter_id(adap)) < 0) { - dev_dbg(&adap->dev, "Unknown adapter ?!?\n"); - return -ENODEV; - } - if (i >= I2CDEV_ADAPS_MAX) { - dev_dbg(&adap->dev, "Adapter number too large?!? (%d)\n",i); - return -ENODEV; - } - + i = i2c_adapter_id(adap); sprintf (name, "i2c/%d", i); - if (! i2cdev_adaps[i]) { - i2cdev_adaps[i] = adap; - devfs_register (NULL, name, + + devfs_register (NULL, name, DEVFS_FL_DEFAULT, I2C_MAJOR, i, S_IFCHR | S_IRUSR | S_IWUSR, &i2cdev_fops, NULL); - dev_dbg(&adap->dev, "Registered as minor %d\n", i); - } else { - /* This is actually a detach_adapter call! */ - devfs_remove("i2c/%d", i); - i2cdev_adaps[i] = NULL; - dev_dbg(&adap->dev, "Adapter unregistered\n"); - } + dev_dbg(&adap->dev, "Registered as minor %d\n", i); + return 0; +} + +int i2cdev_detach_adapter(struct i2c_adapter *adap) +{ + int i; + + i = i2c_adapter_id(adap); + devfs_remove("i2c/%d", i); + dev_dbg(&adap->dev, "Adapter unregistered\n"); return 0; } diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c index ee2eb280f6bc..b060cecc3cbf 100644 --- a/drivers/media/video/dpc7146.c +++ b/drivers/media/video/dpc7146.c @@ -96,7 +96,8 @@ struct dpc static int dpc_probe(struct saa7146_dev* dev) { struct dpc* dpc = 0; - int i = 0; + struct i2c_client *client; + struct list_head *item; dpc = (struct dpc*)kmalloc(sizeof(struct dpc), GFP_KERNEL); if( NULL == dpc ) { @@ -117,12 +118,10 @@ static int dpc_probe(struct saa7146_dev* dev) } /* loop through all i2c-devices on the bus and look who is there */ - for(i = 0; i < I2C_CLIENT_MAX; i++) { - if( NULL == dpc->i2c_adapter.clients[i] ) { - continue; - } - if( I2C_SAA7111A == dpc->i2c_adapter.clients[i]->addr ) - dpc->saa7111a = dpc->i2c_adapter.clients[i]; + list_for_each(item,&dpc->i2c_adapter.clients) { + client = list_entry(item, struct i2c_client, list); + if( I2C_SAA7111A == client->addr ) + dpc->saa7111a = client; } /* check if all devices are present */ diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 96359270ca81..007bba4f4823 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -208,7 +208,8 @@ static int mxb_vbi_bypass(struct saa7146_dev* dev) static int mxb_probe(struct saa7146_dev* dev) { struct mxb* mxb = 0; - int i = 0; + struct i2c_client *client; + struct list_head *item; request_module("tuner"); request_module("tea6420"); @@ -235,22 +236,20 @@ static int mxb_probe(struct saa7146_dev* dev) } /* loop through all i2c-devices on the bus and look who is there */ - for(i = 0; i < I2C_CLIENT_MAX; i++) { - if( NULL == mxb->i2c_adapter.clients[i] ) { - continue; - } - if( I2C_TEA6420_1 == mxb->i2c_adapter.clients[i]->addr ) - mxb->tea6420_1 = mxb->i2c_adapter.clients[i]; - if( I2C_TEA6420_2 == mxb->i2c_adapter.clients[i]->addr ) - mxb->tea6420_2 = mxb->i2c_adapter.clients[i]; - if( I2C_TEA6415C_2 == mxb->i2c_adapter.clients[i]->addr ) - mxb->tea6415c = mxb->i2c_adapter.clients[i]; - if( I2C_TDA9840 == mxb->i2c_adapter.clients[i]->addr ) - mxb->tda9840 = mxb->i2c_adapter.clients[i]; - if( I2C_SAA7111A == mxb->i2c_adapter.clients[i]->addr ) - mxb->saa7111a = mxb->i2c_adapter.clients[i]; - if( 0x60 == mxb->i2c_adapter.clients[i]->addr ) - mxb->tuner = mxb->i2c_adapter.clients[i]; + list_for_each(item,&mxb->i2c_adapter.clients) { + client = list_entry(item, struct i2c_client, list); + if( I2C_TEA6420_1 == client->addr ) + mxb->tea6420_1 = client; + if( I2C_TEA6420_2 == client->addr ) + mxb->tea6420_2 = client; + if( I2C_TEA6415C_2 == client->addr ) + mxb->tea6415c = client; + if( I2C_TDA9840 == client->addr ) + mxb->tda9840 = client; + if( I2C_SAA7111A == client->addr ) + mxb->saa7111a = client; + if( 0x60 == client->addr ) + mxb->tuner = client; } /* check if all devices are present */ diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c index ddc82a5a03fa..100159f3c7e9 100644 --- a/drivers/media/video/tvmixer.c +++ b/drivers/media/video/tvmixer.c @@ -217,8 +217,9 @@ static struct i2c_driver driver = { .owner = THIS_MODULE, .name = "tv card mixer driver", .id = I2C_DRIVERID_TVMIXER, - .flags = I2C_DF_DUMMY, + .flags = I2C_DF_NOTIFY, .attach_adapter = tvmixer_adapters, + .detach_adapter = tvmixer_adapters, .detach_client = tvmixer_clients, }; @@ -234,14 +235,15 @@ static struct file_operations tvmixer_fops = { static int tvmixer_adapters(struct i2c_adapter *adap) { - int i; + struct list_head *item; + struct i2c_client *client; if (debug) printk("tvmixer: adapter %s\n",adap->dev.name); - for (i=0; iclients[i]) - continue; - tvmixer_clients(adap->clients[i]); + + list_for_each(item,&adap->clients) { + client = list_entry(item, struct i2c_client, list); + tvmixer_clients(client); } return 0; } @@ -264,7 +266,8 @@ static int tvmixer_clients(struct i2c_client *client) client->adapter->dev.name); return -1; } - printk("tvmixer: debug: %s\n",client->dev.name); + if (debug) + printk("tvmixer: debug: %s\n",client->dev.name); /* unregister ?? */ for (i = 0; i < DEV_MAX; i++) { diff --git a/include/linux/i2c.h b/include/linux/i2c.h index c506e41b9303..b73144445657 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -39,12 +39,6 @@ /* --- General options ------------------------------------------------ */ -#define I2C_ALGO_MAX 4 /* control memory consumption */ -#define I2C_ADAP_MAX 16 -#define I2C_DRIVER_MAX 16 -#define I2C_CLIENT_MAX 32 -#define I2C_DUMMY_MAX 4 - struct i2c_msg; struct i2c_algorithm; struct i2c_adapter; @@ -131,6 +125,7 @@ struct i2c_driver { * i2c_attach_client. */ int (*attach_adapter)(struct i2c_adapter *); + int (*detach_adapter)(struct i2c_adapter *); /* tells the driver that a client is about to be deleted & gives it * the chance to remove its private data. Also, if the client struct @@ -145,6 +140,7 @@ struct i2c_driver { int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); struct device_driver driver; + struct list_head list; }; #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver) @@ -169,6 +165,7 @@ struct i2c_client { int usage_count; /* How many accesses currently */ /* to the client */ struct device dev; /* the device structure */ + struct list_head list; }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev) @@ -236,12 +233,10 @@ struct i2c_adapter { int (*client_unregister)(struct i2c_client *); /* data fields that are valid for all devices */ - struct semaphore bus; - struct semaphore list; + struct semaphore bus_lock; + struct semaphore clist_lock; unsigned int flags;/* flags specifying div. data */ - struct i2c_client *clients[I2C_CLIENT_MAX]; - int timeout; int retries; struct device dev; /* the adapter device */ @@ -250,6 +245,10 @@ struct i2c_adapter { /* No need to set this when you initialize the adapter */ int inode; #endif /* def CONFIG_PROC_FS */ + + int nr; + struct list_head clients; + struct list_head list; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) @@ -265,7 +264,11 @@ static inline void i2c_set_adapdata (struct i2c_adapter *dev, void *data) /*flags for the driver struct: */ #define I2C_DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ -#define I2C_DF_DUMMY 0x02 /* do not connect any clients */ +#if 0 +/* this flag is gone -- there is a (optional) driver->detach_adapter + * callback now which can be used instead */ +# define I2C_DF_DUMMY 0x02 +#endif /*flags for the client struct: */ #define I2C_CLIENT_ALLOW_USE 0x01 /* Client allows access */ @@ -352,7 +355,8 @@ extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); * or -1 if the adapter was not registered. */ extern int i2c_adapter_id(struct i2c_adapter *adap); - +extern struct i2c_adapter* i2c_get_adapter(int id); +extern void i2c_put_adapter(struct i2c_adapter *adap); /* Return the functionality mask */ -- cgit v1.2.3 From 37d7f421ce2d3ff357ecbed85d20e62bd2e7afcf Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Tue, 6 May 2003 03:16:26 -0700 Subject: [PATCH] i2c #2/3: add i2c_clients_command Changes: * adds a i2c_clients_command() function to i2c-core which calls the ->command() callback of all clients attached to a adapter. * make bttv + saa7134 drivers use that function instead of mucking with the i2c_adapter struct themself. --- drivers/i2c/i2c-core.c | 22 +++++++++++ drivers/media/video/bttv-if.c | 63 +++++++------------------------ drivers/media/video/bttv.h | 1 - drivers/media/video/bttvp.h | 3 +- drivers/media/video/saa7134/saa7134-i2c.c | 23 +++-------- include/linux/i2c.h | 5 +++ 6 files changed, 48 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 690060c4aa8b..88f091f3ae57 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -411,6 +411,27 @@ int i2c_release_client(struct i2c_client *client) return 0; } +void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) +{ + struct list_head *item; + struct i2c_client *client; + + down(&adap->clist_lock); + list_for_each(item,&adap->clients) { + client = list_entry(item, struct i2c_client, list); + if (!try_module_get(client->driver->owner)) + continue; + if (NULL != client->driver->command) { + up(&adap->clist_lock); + client->driver->command(client,cmd,arg); + down(&adap->clist_lock); + } + module_put(client->driver->owner); + } + up(&adap->clist_lock); +} + + /* match always succeeds, as we want the probe() to tell if we really accept this match */ static int i2c_device_match(struct device *dev, struct device_driver *drv) { @@ -1183,6 +1204,7 @@ EXPORT_SYMBOL(i2c_attach_client); EXPORT_SYMBOL(i2c_detach_client); EXPORT_SYMBOL(i2c_use_client); EXPORT_SYMBOL(i2c_release_client); +EXPORT_SYMBOL(i2c_clients_command); EXPORT_SYMBOL(i2c_check_addr); EXPORT_SYMBOL(i2c_master_send); diff --git a/drivers/media/video/bttv-if.c b/drivers/media/video/bttv-if.c index aa00a513ffa9..52b1daa077ba 100644 --- a/drivers/media/video/bttv-if.c +++ b/drivers/media/video/bttv-if.c @@ -7,7 +7,7 @@ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) & Marcus Metzler (mocm@thp.uni-koeln.de) - (c) 1999,2000 Gerd Knorr + (c) 1999-2003 Gerd Knorr 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 @@ -195,51 +195,21 @@ static int bttv_bit_getsda(void *data) static int attach_inform(struct i2c_client *client) { struct bttv *btv = i2c_get_adapdata(client->adapter); - int i; - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (btv->i2c_clients[i] == NULL) { - btv->i2c_clients[i] = client; - break; - } - } - if (btv->tuner_type != -1) + if (btv->tuner_type != UNSET) bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); - if (bttv_verbose) - printk("bttv%d: i2c attach [client=%s,%s]\n",btv->nr, - client->dev.name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct bttv *btv = i2c_get_adapdata(client->adapter); - int i; - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (btv->i2c_clients[i] == client) { - btv->i2c_clients[i] = NULL; - break; - } - } - if (bttv_verbose) - printk("bttv%d: i2c detach [client=%s,%s]\n",btv->nr, - client->dev.name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); + if (bttv_debug) + printk("bttv%d: i2c attach [client=%s]\n", + btv->nr, i2c_clientname(client)); return 0; } void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) { - int i; - - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (NULL == btv->i2c_clients[i]) - continue; - if (NULL == btv->i2c_clients[i]->driver->command) - continue; - btv->i2c_clients[i]->driver->command( - btv->i2c_clients[i],cmd,arg); - } + if (0 != btv->i2c_rc) + return; + i2c_clients_command(&btv->i2c_adap, cmd, arg); } void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg) @@ -260,20 +230,15 @@ static struct i2c_algo_bit_data bttv_i2c_algo_template = { }; static struct i2c_adapter bttv_i2c_adap_template = { - .owner = THIS_MODULE, + .owner = THIS_MODULE, + I2C_DEVNAME("bt848"), .id = I2C_HW_B_BT848, .client_register = attach_inform, - .client_unregister = detach_inform, - .dev = { - .name = "bt848", - }, }; static struct i2c_client bttv_i2c_client_template = { - .id = -1, - .dev = { - .name = "bttv internal", - }, + I2C_DEVNAME("bttv internal"), + .id = -1, }; @@ -347,8 +312,8 @@ int __devinit init_bttv_i2c(struct bttv *btv) memcpy(&btv->i2c_client, &bttv_i2c_client_template, sizeof(struct i2c_client)); - sprintf(btv->i2c_adap.dev.name+strlen(btv->i2c_adap.dev.name), - " #%d", btv->nr); + sprintf(btv->i2c_adap.dev.name, "bt848 #%d", btv->nr); + btv->i2c_algo.data = btv; i2c_set_adapdata(&btv->i2c_adap, btv); btv->i2c_adap.algo_data = &btv->i2c_algo; diff --git a/drivers/media/video/bttv.h b/drivers/media/video/bttv.h index 79eaa3ed9e27..230cb26bcf77 100644 --- a/drivers/media/video/bttv.h +++ b/drivers/media/video/bttv.h @@ -243,7 +243,6 @@ extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg); /* i2c */ -#define I2C_CLIENTS_MAX 16 extern void bttv_bit_setscl(void *data, int state); extern void bttv_bit_setsda(void *data, int state); extern void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg); diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 65f8d54e1816..dd44733fadb6 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -62,6 +62,8 @@ #define RAW_LINES 640 #define RAW_BPL 1024 +#define UNSET (-1U) + /* ---------------------------------------------------------- */ struct bttv_tvnorm @@ -276,7 +278,6 @@ struct bttv { struct i2c_algo_bit_data i2c_algo; struct i2c_client i2c_client; int i2c_state, i2c_rc; - struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; /* video4linux (1) */ struct video_device video_dev; diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 2fce1446dac8..0aa4292d4a51 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -334,19 +334,15 @@ static struct i2c_algorithm saa7134_algo = { static struct i2c_adapter saa7134_adap_template = { .owner = THIS_MODULE, + I2C_DEVNAME("saa7134"), .id = I2C_ALGO_SAA7134, .algo = &saa7134_algo, .client_register = attach_inform, - .dev = { - .name = "saa7134", - }, }; static struct i2c_client saa7134_client_template = { - .id = -1, - .dev = { - .name = "saa7134 internal", - }, + I2C_DEVNAME("saa7134 internal"), + .id = -1, }; /* ----------------------------------------------------------- */ @@ -399,22 +395,13 @@ saa7134_i2c_scan(struct saa7134_dev *dev) void saa7134_i2c_call_clients(struct saa7134_dev *dev, unsigned int cmd, void *arg) { - int i; - - for (i = 0; i < I2C_CLIENT_MAX; i++) { - if (NULL == dev->i2c_adap.clients[i]) - continue; - if (NULL == dev->i2c_adap.clients[i]->driver->command) - continue; - dev->i2c_adap.clients[i]->driver->command - (dev->i2c_adap.clients[i],cmd,arg); - } + i2c_clients_command(&dev->i2c_adap, cmd, arg); } int saa7134_i2c_register(struct saa7134_dev *dev) { dev->i2c_adap = saa7134_adap_template; - strncpy(dev->i2c_adap.dev.name, dev->name, DEVICE_NAME_SIZE); + strcpy(dev->i2c_adap.dev.name,dev->name); dev->i2c_adap.algo_data = dev; i2c_add_adapter(&dev->i2c_adap); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index b73144445657..15c94caeba74 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -334,6 +334,11 @@ extern struct i2c_client *i2c_get_client(int driver_id, int adapter_id, extern int i2c_use_client(struct i2c_client *); extern int i2c_release_client(struct i2c_client *); +/* call the i2c_client->command() of all attached clients with + * the given arguments */ +extern void i2c_clients_command(struct i2c_adapter *adap, + unsigned int cmd, void *arg); + /* returns -EBUSY if address has been taken, 0 if not. Note that the only other place at which this is called is within i2c_attach_client; so you can cheat by simply not registering. Not recommended, of course! */ -- cgit v1.2.3 From ea1b28a4d66b08082242670e717d92b86b52b46d Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Tue, 6 May 2003 03:16:36 -0700 Subject: [PATCH] i2c #3/3: add class field to i2c_adapter This is the last of three patches for i2c. It introduces a new field to i2c_adapter which classifies the kind of hardware a i2c adapter belongs to (analog tv card / dvb card / smbus / gfx card ...). i2c chip drivers can use this infomation to decide whenever they want to look for hardware on that adapter or not. It doesn't make sense to probe for a tv tuner on a smbus for example ... --- drivers/i2c/busses/i2c-ali15x3.c | 1 + drivers/i2c/busses/i2c-amd756.c | 1 + drivers/i2c/busses/i2c-amd8111.c | 1 + drivers/i2c/busses/i2c-i801.c | 1 + drivers/i2c/busses/i2c-isa.c | 1 + drivers/i2c/busses/i2c-piix4.c | 1 + drivers/i2c/busses/i2c-viapro.c | 1 + drivers/i2c/chips/adm1021.c | 2 ++ drivers/i2c/chips/it87.c | 2 ++ drivers/i2c/chips/lm75.c | 2 ++ drivers/i2c/chips/via686a.c | 2 ++ drivers/i2c/chips/w83781d.c | 2 ++ drivers/media/video/bt832.c | 22 +++------------------- drivers/media/video/bttv-if.c | 1 + drivers/media/video/msp3400.c | 2 +- drivers/media/video/saa5249.c | 6 +----- drivers/media/video/saa7134/saa7134-i2c.c | 1 + drivers/media/video/tda7432.c | 2 +- drivers/media/video/tda9875.c | 2 +- drivers/media/video/tda9887.c | 20 +++----------------- drivers/media/video/tuner.c | 22 ++++------------------ drivers/media/video/tvaudio.c | 9 ++------- drivers/media/video/tvmixer.c | 7 +------ include/linux/i2c.h | 7 +++++++ 24 files changed, 43 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index e431e9fb3f68..1ca200036209 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -475,6 +475,7 @@ static struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter ali15x3_adapter = { .owner = THIS_MODULE, .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3, + .class = I2C_ADAP_CLASS_SMBUS, .algo = &smbus_algorithm, .dev = { .name = "unset", diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index 8515937334a7..61f1ad0c86ae 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -313,6 +313,7 @@ static struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter amd756_adapter = { .owner = THIS_MODULE, .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756, + .class = I2C_ADAP_CLASS_SMBUS, .algo = &smbus_algorithm, .dev = { .name = "unset", diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index f852936a8291..a87dc0b40c5a 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -360,6 +360,7 @@ static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_ snprintf(smbus->adapter.dev.name, DEVICE_NAME_SIZE, "SMBus2 AMD8111 adapter at %04x", smbus->base); smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD8111; + smbus->adapter.class = I2C_ADAP_CLASS_SMBUS; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 7a208e66d006..701520b0c86f 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -547,6 +547,7 @@ static struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter i801_adapter = { .owner = THIS_MODULE, .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801, + .class = I2C_ADAP_CLASS_SMBUS, .algo = &smbus_algorithm, .dev = { .name = "unset", diff --git a/drivers/i2c/busses/i2c-isa.c b/drivers/i2c/busses/i2c-isa.c index 851da94882ab..c49a874764e5 100644 --- a/drivers/i2c/busses/i2c-isa.c +++ b/drivers/i2c/busses/i2c-isa.c @@ -40,6 +40,7 @@ static struct i2c_algorithm isa_algorithm = { static struct i2c_adapter isa_adapter = { .owner = THIS_MODULE, .id = I2C_ALGO_ISA | I2C_HW_ISA, + .class = I2C_ADAP_CLASS_SMBUS, .algo = &isa_algorithm, .dev = { .name = "ISA main adapter", diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index d7e0f720a9ec..a7bd53bfb10c 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -395,6 +395,7 @@ static struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter piix4_adapter = { .owner = THIS_MODULE, .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4, + .class = I2C_ADAP_CLASS_SMBUS, .algo = &smbus_algorithm, .dev = { .name = "unset", diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 05b54382d6c1..12742967bfec 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -295,6 +295,7 @@ static struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter vt596_adapter = { .owner = THIS_MODULE, .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2, + .class = I2C_ADAP_CLASS_SMBUS, .algo = &smbus_algorithm, .dev = { .name = "unset", diff --git a/drivers/i2c/chips/adm1021.c b/drivers/i2c/chips/adm1021.c index f7afe0b4b4ea..2db50c2a2ad4 100644 --- a/drivers/i2c/chips/adm1021.c +++ b/drivers/i2c/chips/adm1021.c @@ -203,6 +203,8 @@ static DEVICE_ATTR(die_code, S_IRUGO, show_die_code, NULL); static int adm1021_attach_adapter(struct i2c_adapter *adapter) { + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; return i2c_detect(adapter, &addr_data, adm1021_detect); } diff --git a/drivers/i2c/chips/it87.c b/drivers/i2c/chips/it87.c index 73f8ec4433c9..b853e8233d8f 100644 --- a/drivers/i2c/chips/it87.c +++ b/drivers/i2c/chips/it87.c @@ -525,6 +525,8 @@ static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, NULL); * when a new adapter is inserted (and it87_driver is still present) */ static int it87_attach_adapter(struct i2c_adapter *adapter) { + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; return i2c_detect(adapter, &addr_data, it87_detect); } diff --git a/drivers/i2c/chips/lm75.c b/drivers/i2c/chips/lm75.c index f4acc1649917..1214ab3f4ca5 100644 --- a/drivers/i2c/chips/lm75.c +++ b/drivers/i2c/chips/lm75.c @@ -121,6 +121,8 @@ static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL); static int lm75_attach_adapter(struct i2c_adapter *adapter) { + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; return i2c_detect(adapter, &addr_data, lm75_detect); } diff --git a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c index 83859fead850..664e521a6d61 100644 --- a/drivers/i2c/chips/via686a.c +++ b/drivers/i2c/chips/via686a.c @@ -661,6 +661,8 @@ static struct i2c_driver via686a_driver = { /* This is called when the module is loaded */ static int via686a_attach_adapter(struct i2c_adapter *adapter) { + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; return i2c_detect(adapter, &addr_data, via686a_detect); } diff --git a/drivers/i2c/chips/w83781d.c b/drivers/i2c/chips/w83781d.c index c1fbf4c72873..5da55f2acdfc 100644 --- a/drivers/i2c/chips/w83781d.c +++ b/drivers/i2c/chips/w83781d.c @@ -1026,6 +1026,8 @@ device_create_file(&client->dev, &dev_attr_rt##offset); \ static int w83781d_attach_adapter(struct i2c_adapter *adapter) { + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; return i2c_detect(adapter, &addr_data, w83781d_detect); } diff --git a/drivers/media/video/bt832.c b/drivers/media/video/bt832.c index 6faa2abec3ae..70ef06611498 100644 --- a/drivers/media/video/bt832.c +++ b/drivers/media/video/bt832.c @@ -198,25 +198,9 @@ static int bt832_attach(struct i2c_adapter *adap, int addr, static int bt832_probe(struct i2c_adapter *adap) { - int rc; - - printk("bt832_probe\n"); - - switch (adap->id) { - case I2C_ALGO_BIT | I2C_HW_B_BT848: - case I2C_ALGO_BIT | I2C_HW_B_RIVA: - case I2C_ALGO_SAA7134: - printk("bt832: probing %s i2c adapter [id=0x%x]\n", - adap->name,adap->id); - rc = i2c_probe(adap, &addr_data, bt832_attach); - break; - default: - printk("bt832: ignoring %s i2c adapter [id=0x%x]\n", - adap->name,adap->id); - rc = 0; - /* nothing */ - } - return rc; + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) + return i2c_probe(adap, &addr_data, bt832_attach); + return 0; } static int bt832_detach(struct i2c_client *client) diff --git a/drivers/media/video/bttv-if.c b/drivers/media/video/bttv-if.c index 52b1daa077ba..a4a462ecb2fa 100644 --- a/drivers/media/video/bttv-if.c +++ b/drivers/media/video/bttv-if.c @@ -233,6 +233,7 @@ static struct i2c_adapter bttv_i2c_adap_template = { .owner = THIS_MODULE, I2C_DEVNAME("bt848"), .id = I2C_HW_B_BT848, + .class = I2C_ADAP_CLASS_TV_ANALOG, .client_register = attach_inform, }; diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c index d81070e1b156..65403235f802 100644 --- a/drivers/media/video/msp3400.c +++ b/drivers/media/video/msp3400.c @@ -1372,7 +1372,7 @@ static int msp_detach(struct i2c_client *client) static int msp_probe(struct i2c_adapter *adap) { - if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, msp_attach); return 0; } diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index df511ae63a8c..97c3990e02ae 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -224,12 +224,8 @@ static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) static int saa5249_probe(struct i2c_adapter *adap) { - /* Only attach these chips to the BT848 bus for now */ - - if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) - { + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, saa5249_attach); - } return 0; } diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 0aa4292d4a51..b1249c3aca96 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -336,6 +336,7 @@ static struct i2c_adapter saa7134_adap_template = { .owner = THIS_MODULE, I2C_DEVNAME("saa7134"), .id = I2C_ALGO_SAA7134, + .class = I2C_ADAP_CLASS_TV_ANALOG, .algo = &saa7134_algo, .client_register = attach_inform, }; diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index eaf5f5c8eef2..61454f84ff7f 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -340,7 +340,7 @@ static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind) static int tda7432_probe(struct i2c_adapter *adap) { - if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, tda7432_attach); return 0; } diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 0e97185a9b68..bcb44135bef2 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -273,7 +273,7 @@ static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind) static int tda9875_probe(struct i2c_adapter *adap) { - if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, tda9875_attach); return 0; } diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index a674ff9f15c5..d8aecd8acb44 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -368,23 +368,9 @@ static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) static int tda9887_probe(struct i2c_adapter *adap) { - int rc; - - switch (adap->id) { - case I2C_ALGO_BIT | I2C_HW_B_BT848: - case I2C_ALGO_BIT | I2C_HW_B_RIVA: - case I2C_ALGO_SAA7134: - printk("tda9887: probing %s i2c adapter [id=0x%x]\n", - adap->dev.name,adap->id); - rc = i2c_probe(adap, &addr_data, tda9887_attach); - break; - default: - printk("tda9887: ignoring %s i2c adapter [id=0x%x]\n", - adap->dev.name,adap->id); - rc = 0; - /* nothing */ - } - return rc; + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) + return i2c_probe(adap, &addr_data, tda9887_attach); + return 0; } static int tda9887_detach(struct i2c_client *client) diff --git a/drivers/media/video/tuner.c b/drivers/media/video/tuner.c index 93de9a356f21..b193c4c195bc 100644 --- a/drivers/media/video/tuner.c +++ b/drivers/media/video/tuner.c @@ -817,29 +817,15 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) static int tuner_probe(struct i2c_adapter *adap) { - int rc; - if (0 != addr) { normal_i2c_range[0] = addr; normal_i2c_range[1] = addr; } this_adap = 0; - switch (adap->id) { - case I2C_ALGO_BIT | I2C_HW_B_BT848: - case I2C_ALGO_BIT | I2C_HW_B_RIVA: - case I2C_ALGO_SAA7134: - case I2C_ALGO_SAA7146: - printk("tuner: probing %s i2c adapter [id=0x%x]\n", - adap->dev.name,adap->id); - rc = i2c_probe(adap, &addr_data, tuner_attach); - break; - default: - printk("tuner: ignoring %s i2c adapter [id=0x%x]\n", - adap->dev.name,adap->id); - rc = 0; - /* nothing */ - } - return rc; + + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) + return i2c_probe(adap, &addr_data, tuner_attach); + return 0; } static int tuner_detach(struct i2c_client *client) diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index d546d6d72a14..0aaba6c19bb8 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1408,14 +1408,9 @@ static int chip_attach(struct i2c_adapter *adap, int addr, int kind) static int chip_probe(struct i2c_adapter *adap) { - switch (adap->id) { - case I2C_ALGO_BIT | I2C_HW_B_BT848: - case I2C_ALGO_BIT | I2C_HW_B_RIVA: + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, chip_attach); - default: - /* ignore this i2c bus */ - return 0; - } + return 0; } static int chip_detach(struct i2c_client *client) diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c index 100159f3c7e9..7f244b001cb6 100644 --- a/drivers/media/video/tvmixer.c +++ b/drivers/media/video/tvmixer.c @@ -254,12 +254,7 @@ static int tvmixer_clients(struct i2c_client *client) int i,minor; /* TV card ??? */ - switch (client->adapter->id) { - case I2C_ALGO_BIT | I2C_HW_B_BT848: - case I2C_ALGO_BIT | I2C_HW_B_RIVA: - /* ok, have a look ... */ - break; - default: + if (!(client->adapter->class & I2C_ADAP_CLASS_TV_ANALOG)) { /* ignore that one */ if (debug) printk("tvmixer: %s is not a tv card\n", diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 15c94caeba74..6876a6705bb8 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -225,6 +225,7 @@ struct i2c_adapter { struct module *owner; unsigned int id;/* == is algo->id | hwdep.struct->id, */ /* for registered values see below */ + unsigned int class; struct i2c_algorithm *algo;/* the algorithm to access the bus */ void *algo_data; @@ -278,6 +279,12 @@ static inline void i2c_set_adapdata (struct i2c_adapter *dev, void *data) #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ /* Must equal I2C_M_TEN below */ +/* i2c adapter classes (bitmask) */ +#define I2C_ADAP_CLASS_SMBUS (1<<0) /* lm_sensors, ... */ +#define I2C_ADAP_CLASS_TV_ANALOG (1<<1) /* bttv + friends */ +#define I2C_ADAP_CLASS_TV_DIGINAL (1<<2) /* dbv cards */ +#define I2C_ADAP_CLASS_DDC (1<<3) /* i2c-matroxfb ? */ + /* i2c_client_address_data is the struct for holding default client * addresses for a driver and for the parameters supplied on the * command line -- cgit v1.2.3