diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/irda/irlan/irlan_eth.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/irda/irlan/irlan_eth.c')
-rw-r--r-- | net/irda/irlan/irlan_eth.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c new file mode 100644 index 000000000000..071cd2cefd8a --- /dev/null +++ b/net/irda/irlan/irlan_eth.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /********************************************************************* | ||
2 | * | ||
3 | * Filename: irlan_eth.c | ||
4 | * Version: | ||
5 | * Description: | ||
6 | * Status: Experimental. | ||
7 | * Author: Dag Brattli <dagb@cs.uit.no> | ||
8 | * Created at: Thu Oct 15 08:37:58 1998 | ||
9 | * Modified at: Tue Mar 21 09:06:41 2000 | ||
10 | * Modified by: Dag Brattli <dagb@cs.uit.no> | ||
11 | * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov> | ||
12 | * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> | ||
13 | * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> | ||
14 | * | ||
15 | * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or | ||
18 | * modify it under the terms of the GNU General Public License as | ||
19 | * published by the Free Software Foundation; either version 2 of | ||
20 | * the License, or (at your option) any later version. | ||
21 | * | ||
22 | * Neither Dag Brattli nor University of Tromsų admit liability nor | ||
23 | * provide warranty for any of this software. This material is | ||
24 | * provided "AS-IS" and at no charge. | ||
25 | * | ||
26 | ********************************************************************/ | ||
27 | |||
28 | #include <linux/config.h> | ||
29 | #include <linux/netdevice.h> | ||
30 | #include <linux/etherdevice.h> | ||
31 | #include <linux/inetdevice.h> | ||
32 | #include <linux/if_arp.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <net/arp.h> | ||
35 | |||
36 | #include <net/irda/irda.h> | ||
37 | #include <net/irda/irmod.h> | ||
38 | #include <net/irda/irlan_common.h> | ||
39 | #include <net/irda/irlan_client.h> | ||
40 | #include <net/irda/irlan_event.h> | ||
41 | #include <net/irda/irlan_eth.h> | ||
42 | |||
43 | static int irlan_eth_open(struct net_device *dev); | ||
44 | static int irlan_eth_close(struct net_device *dev); | ||
45 | static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev); | ||
46 | static void irlan_eth_set_multicast_list( struct net_device *dev); | ||
47 | static struct net_device_stats *irlan_eth_get_stats(struct net_device *dev); | ||
48 | |||
49 | /* | ||
50 | * Function irlan_eth_setup (dev) | ||
51 | * | ||
52 | * The network device initialization function. | ||
53 | * | ||
54 | */ | ||
55 | static void irlan_eth_setup(struct net_device *dev) | ||
56 | { | ||
57 | dev->open = irlan_eth_open; | ||
58 | dev->stop = irlan_eth_close; | ||
59 | dev->hard_start_xmit = irlan_eth_xmit; | ||
60 | dev->get_stats = irlan_eth_get_stats; | ||
61 | dev->set_multicast_list = irlan_eth_set_multicast_list; | ||
62 | dev->destructor = free_netdev; | ||
63 | |||
64 | SET_MODULE_OWNER(dev); | ||
65 | |||
66 | ether_setup(dev); | ||
67 | |||
68 | /* | ||
69 | * Lets do all queueing in IrTTP instead of this device driver. | ||
70 | * Queueing here as well can introduce some strange latency | ||
71 | * problems, which we will avoid by setting the queue size to 0. | ||
72 | */ | ||
73 | /* | ||
74 | * The bugs in IrTTP and IrLAN that created this latency issue | ||
75 | * have now been fixed, and we can propagate flow control properly | ||
76 | * to the network layer. However, this requires a minimal queue of | ||
77 | * packets for the device. | ||
78 | * Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14 | ||
79 | * With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11 | ||
80 | * See irlan_eth_flow_indication()... | ||
81 | * Note : this number was randomly selected and would need to | ||
82 | * be adjusted. | ||
83 | * Jean II */ | ||
84 | dev->tx_queue_len = 4; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * Function alloc_irlandev | ||
89 | * | ||
90 | * Allocate network device and control block | ||
91 | * | ||
92 | */ | ||
93 | struct net_device *alloc_irlandev(const char *name) | ||
94 | { | ||
95 | return alloc_netdev(sizeof(struct irlan_cb), name, | ||
96 | irlan_eth_setup); | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * Function irlan_eth_open (dev) | ||
101 | * | ||
102 | * Network device has been opened by user | ||
103 | * | ||
104 | */ | ||
105 | static int irlan_eth_open(struct net_device *dev) | ||
106 | { | ||
107 | struct irlan_cb *self = netdev_priv(dev); | ||
108 | |||
109 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
110 | |||
111 | /* Ready to play! */ | ||
112 | netif_stop_queue(dev); /* Wait until data link is ready */ | ||
113 | |||
114 | /* We are now open, so time to do some work */ | ||
115 | self->disconnect_reason = 0; | ||
116 | irlan_client_wakeup(self, self->saddr, self->daddr); | ||
117 | |||
118 | /* Make sure we have a hardware address before we return, | ||
119 | so DHCP clients gets happy */ | ||
120 | return wait_event_interruptible(self->open_wait, | ||
121 | !self->tsap_data->connected); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * Function irlan_eth_close (dev) | ||
126 | * | ||
127 | * Stop the ether network device, his function will usually be called by | ||
128 | * ifconfig down. We should now disconnect the link, We start the | ||
129 | * close timer, so that the instance will be removed if we are unable | ||
130 | * to discover the remote device after the disconnect. | ||
131 | */ | ||
132 | static int irlan_eth_close(struct net_device *dev) | ||
133 | { | ||
134 | struct irlan_cb *self = netdev_priv(dev); | ||
135 | |||
136 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
137 | |||
138 | /* Stop device */ | ||
139 | netif_stop_queue(dev); | ||
140 | |||
141 | irlan_close_data_channel(self); | ||
142 | irlan_close_tsaps(self); | ||
143 | |||
144 | irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); | ||
145 | irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); | ||
146 | |||
147 | /* Remove frames queued on the control channel */ | ||
148 | skb_queue_purge(&self->client.txq); | ||
149 | |||
150 | self->client.tx_busy = 0; | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * Function irlan_eth_tx (skb) | ||
157 | * | ||
158 | * Transmits ethernet frames over IrDA link. | ||
159 | * | ||
160 | */ | ||
161 | static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev) | ||
162 | { | ||
163 | struct irlan_cb *self = netdev_priv(dev); | ||
164 | int ret; | ||
165 | |||
166 | /* skb headroom large enough to contain all IrDA-headers? */ | ||
167 | if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) { | ||
168 | struct sk_buff *new_skb = | ||
169 | skb_realloc_headroom(skb, self->max_header_size); | ||
170 | |||
171 | /* We have to free the original skb anyway */ | ||
172 | dev_kfree_skb(skb); | ||
173 | |||
174 | /* Did the realloc succeed? */ | ||
175 | if (new_skb == NULL) | ||
176 | return 0; | ||
177 | |||
178 | /* Use the new skb instead */ | ||
179 | skb = new_skb; | ||
180 | } | ||
181 | |||
182 | dev->trans_start = jiffies; | ||
183 | |||
184 | /* Now queue the packet in the transport layer */ | ||
185 | if (self->use_udata) | ||
186 | ret = irttp_udata_request(self->tsap_data, skb); | ||
187 | else | ||
188 | ret = irttp_data_request(self->tsap_data, skb); | ||
189 | |||
190 | if (ret < 0) { | ||
191 | /* | ||
192 | * IrTTPs tx queue is full, so we just have to | ||
193 | * drop the frame! You might think that we should | ||
194 | * just return -1 and don't deallocate the frame, | ||
195 | * but that is dangerous since it's possible that | ||
196 | * we have replaced the original skb with a new | ||
197 | * one with larger headroom, and that would really | ||
198 | * confuse do_dev_queue_xmit() in dev.c! I have | ||
199 | * tried :-) DB | ||
200 | */ | ||
201 | /* irttp_data_request already free the packet */ | ||
202 | self->stats.tx_dropped++; | ||
203 | } else { | ||
204 | self->stats.tx_packets++; | ||
205 | self->stats.tx_bytes += skb->len; | ||
206 | } | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Function irlan_eth_receive (handle, skb) | ||
213 | * | ||
214 | * This function gets the data that is received on the data channel | ||
215 | * | ||
216 | */ | ||
217 | int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb) | ||
218 | { | ||
219 | struct irlan_cb *self = instance; | ||
220 | |||
221 | if (skb == NULL) { | ||
222 | ++self->stats.rx_dropped; | ||
223 | return 0; | ||
224 | } | ||
225 | if (skb->len < ETH_HLEN) { | ||
226 | IRDA_DEBUG(0, "%s() : IrLAN frame too short (%d)\n", | ||
227 | __FUNCTION__, skb->len); | ||
228 | ++self->stats.rx_dropped; | ||
229 | dev_kfree_skb(skb); | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * Adopt this frame! Important to set all these fields since they | ||
235 | * might have been previously set by the low level IrDA network | ||
236 | * device driver | ||
237 | */ | ||
238 | skb->dev = self->dev; | ||
239 | skb->protocol=eth_type_trans(skb, skb->dev); /* Remove eth header */ | ||
240 | |||
241 | self->stats.rx_packets++; | ||
242 | self->stats.rx_bytes += skb->len; | ||
243 | |||
244 | netif_rx(skb); /* Eat it! */ | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Function irlan_eth_flow (status) | ||
251 | * | ||
252 | * Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by | ||
253 | * controlling the queue stop/start. | ||
254 | * | ||
255 | * The IrDA link layer has the advantage to have flow control, and | ||
256 | * IrTTP now properly handles that. Flow controlling the higher layers | ||
257 | * prevent us to drop Tx packets in here (up to 15% for a TCP socket, | ||
258 | * more for UDP socket). | ||
259 | * Also, this allow us to reduce the overall transmit queue, which means | ||
260 | * less latency in case of mixed traffic. | ||
261 | * Jean II | ||
262 | */ | ||
263 | void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) | ||
264 | { | ||
265 | struct irlan_cb *self; | ||
266 | struct net_device *dev; | ||
267 | |||
268 | self = (struct irlan_cb *) instance; | ||
269 | |||
270 | IRDA_ASSERT(self != NULL, return;); | ||
271 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | ||
272 | |||
273 | dev = self->dev; | ||
274 | |||
275 | IRDA_ASSERT(dev != NULL, return;); | ||
276 | |||
277 | IRDA_DEBUG(0, "%s() : flow %s ; running %d\n", __FUNCTION__, | ||
278 | flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START", | ||
279 | netif_running(dev)); | ||
280 | |||
281 | switch (flow) { | ||
282 | case FLOW_STOP: | ||
283 | /* IrTTP is full, stop higher layers */ | ||
284 | netif_stop_queue(dev); | ||
285 | break; | ||
286 | case FLOW_START: | ||
287 | default: | ||
288 | /* Tell upper layers that its time to transmit frames again */ | ||
289 | /* Schedule network layer */ | ||
290 | netif_wake_queue(dev); | ||
291 | break; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * Function irlan_etc_send_gratuitous_arp (dev) | ||
297 | * | ||
298 | * Send gratuitous ARP to announce that we have changed | ||
299 | * hardware address, so that all peers updates their ARP tables | ||
300 | */ | ||
301 | void irlan_eth_send_gratuitous_arp(struct net_device *dev) | ||
302 | { | ||
303 | struct in_device *in_dev; | ||
304 | |||
305 | /* | ||
306 | * When we get a new MAC address do a gratuitous ARP. This | ||
307 | * is useful if we have changed access points on the same | ||
308 | * subnet. | ||
309 | */ | ||
310 | #ifdef CONFIG_INET | ||
311 | IRDA_DEBUG(4, "IrLAN: Sending gratuitous ARP\n"); | ||
312 | rcu_read_lock(); | ||
313 | in_dev = __in_dev_get(dev); | ||
314 | if (in_dev == NULL) | ||
315 | goto out; | ||
316 | if (in_dev->ifa_list) | ||
317 | |||
318 | arp_send(ARPOP_REQUEST, ETH_P_ARP, | ||
319 | in_dev->ifa_list->ifa_address, | ||
320 | dev, | ||
321 | in_dev->ifa_list->ifa_address, | ||
322 | NULL, dev->dev_addr, NULL); | ||
323 | out: | ||
324 | rcu_read_unlock(); | ||
325 | #endif /* CONFIG_INET */ | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Function set_multicast_list (dev) | ||
330 | * | ||
331 | * Configure the filtering of the device | ||
332 | * | ||
333 | */ | ||
334 | #define HW_MAX_ADDRS 4 /* Must query to get it! */ | ||
335 | static void irlan_eth_set_multicast_list(struct net_device *dev) | ||
336 | { | ||
337 | struct irlan_cb *self = netdev_priv(dev); | ||
338 | |||
339 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
340 | |||
341 | /* Check if data channel has been connected yet */ | ||
342 | if (self->client.state != IRLAN_DATA) { | ||
343 | IRDA_DEBUG(1, "%s(), delaying!\n", __FUNCTION__ ); | ||
344 | return; | ||
345 | } | ||
346 | |||
347 | if (dev->flags & IFF_PROMISC) { | ||
348 | /* Enable promiscuous mode */ | ||
349 | IRDA_WARNING("Promiscous mode not implemented by IrLAN!\n"); | ||
350 | } | ||
351 | else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS) { | ||
352 | /* Disable promiscuous mode, use normal mode. */ | ||
353 | IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __FUNCTION__ ); | ||
354 | /* hardware_set_filter(NULL); */ | ||
355 | |||
356 | irlan_set_multicast_filter(self, TRUE); | ||
357 | } | ||
358 | else if (dev->mc_count) { | ||
359 | IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __FUNCTION__ ); | ||
360 | /* Walk the address list, and load the filter */ | ||
361 | /* hardware_set_filter(dev->mc_list); */ | ||
362 | |||
363 | irlan_set_multicast_filter(self, TRUE); | ||
364 | } | ||
365 | else { | ||
366 | IRDA_DEBUG(4, "%s(), Clearing multicast filter\n", __FUNCTION__ ); | ||
367 | irlan_set_multicast_filter(self, FALSE); | ||
368 | } | ||
369 | |||
370 | if (dev->flags & IFF_BROADCAST) | ||
371 | irlan_set_broadcast_filter(self, TRUE); | ||
372 | else | ||
373 | irlan_set_broadcast_filter(self, FALSE); | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Function irlan_get_stats (dev) | ||
378 | * | ||
379 | * Get the current statistics for this device | ||
380 | * | ||
381 | */ | ||
382 | static struct net_device_stats *irlan_eth_get_stats(struct net_device *dev) | ||
383 | { | ||
384 | struct irlan_cb *self = netdev_priv(dev); | ||
385 | |||
386 | return &self->stats; | ||
387 | } | ||