]>
Commit | Line | Data |
---|---|---|
8b732bb7 | 1 | diff -urN linux-2.4.25/drivers/atm/Makefile linux-2.4.25-atmdd/drivers/atm/Makefile |
2 | --- linux-2.4.25/drivers/atm/Makefile 2004-02-23 15:18:29.000000000 +0100 | |
3 | +++ linux-2.4.25-atmdd/drivers/atm/Makefile 2004-02-29 22:51:26.000000000 +0100 | |
4 | @@ -31,6 +31,7 @@ | |
8b732bb7 | 5 | endif |
6 | ||
e0ed9d74 | 7 | obj-$(CONFIG_ATM_DUMMY) += adummy.o |
8b732bb7 | 8 | +obj-$(CONFIG_ATM_DD) += atmdd.o |
9 | obj-$(CONFIG_ATM_TCP) += atmtcp.o | |
10 | obj-$(CONFIG_ATM_FIRESTREAM) += firestream.o | |
11 | obj-$(CONFIG_ATM_LANAI) += lanai.o | |
12 | diff -urN linux-2.4.25/drivers/atm/Kconfig linux-2.4.25-atmdd/drivers/atm/Kconfig | |
13 | --- linux-2.4.25/drivers/atm/Kcnfig 2003-08-25 13:44:41.000000000 +0200 | |
14 | +++ linux-2.4.25-atmdd/drivers/atm/Kconfig 2004-02-29 22:52:59.000000000 +0100 | |
15 | @@ -4,6 +4,14 @@ | |
16 | ||
17 | menu "ATM drivers" | |
18 | depends on NETDEVICES && ATM | |
19 | + | |
20 | +config ATM_DD | |
21 | + tristate "ATM loopback" | |
22 | + depends on INET && ATM | |
23 | + help | |
24 | + This is an example atm driver. It does not require any actual ATM | |
25 | + hardware. It supports AAL5 and AAL0. Frames are merely looped back | |
26 | + to the sender on the same VC they were sent. | |
27 | ||
28 | config ATM_TCP | |
29 | tristate "ATM over TCP" | |
30 | diff -urN linux-2.4.25/drivers/atm/atmdd.c linux-2.4.25-atmdd/drivers/atm/atmdd.c | |
31 | --- linux-2.4.25/drivers/atm/atmdd.c 1970-01-01 01:00:00.000000000 +0100 | |
32 | +++ linux-2.4.25-atmdd/drivers/atm/atmdd.c 2004-02-29 22:58:11.000000000 +0100 | |
8ce7bd68 | 33 | @@ -0,0 +1,922 @@ |
8b732bb7 | 34 | +/* |
35 | +####################################################################### | |
36 | +# | |
37 | +# (C) Copyright 2001 | |
38 | +# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com | |
39 | +# | |
40 | +# This program is free software; you can redistribute it and/or | |
41 | +# modify it under the terms of the GNU General Public License as | |
42 | +# published by the Free Software Foundation; either version 2 of | |
43 | +# the License, or (at your option) any later version. | |
44 | +# | |
45 | +# This program is distributed in the hope that it will be useful, | |
46 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
47 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
48 | +# GNU General Public License for more details. | |
49 | +# | |
50 | +# You should have received a copy of the GNU General Public License | |
51 | +# along with this program; if not, write to the Free Software | |
52 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
53 | +# MA 02111-1307 USA | |
54 | +####################################################################### | |
55 | +# Notes: | |
56 | +# | |
57 | +# This is an example atm driver. It does not require any actual ATM | |
58 | +# hardware. It supports AAL5 and AAL0. frames are merely looped back | |
59 | +# to the sender on the same VC they were sent. | |
60 | +# | |
61 | +####################################################################### | |
62 | +*/ | |
63 | + | |
64 | +/*############ Includes ###############################################*/ | |
65 | + | |
66 | +#include <linux/module.h> | |
67 | +#include <linux/config.h> | |
68 | +#include <linux/kernel.h> | |
69 | +#include <linux/errno.h> | |
70 | +#include <linux/atm.h> | |
71 | +#include <linux/atmdev.h> | |
72 | +#include <linux/skbuff.h> | |
73 | +#include <linux/init.h> | |
74 | +#include <linux/netdevice.h> | |
75 | +#include <linux/sched.h> /* for xtime */ | |
76 | + | |
77 | +/*############ Defines ################################################*/ | |
78 | + | |
79 | +#define MYATMDD "atmdd" | |
80 | +#define KLOG_PREAMBLE MYATMDD ": " | |
81 | +#define MYATMDD_VPI_BITS 1 /* Allow ?.1.? but not ?.2.? */ | |
82 | +#define MYATMDD_VCI_BITS 11 /* Allow ?.?.2047 but not ?.?.2048 */ | |
83 | +#define MYATMDD_PCR 100000 | |
84 | +#define RXQ_SZ 16 | |
85 | +#define TXQ_SZ 16 | |
86 | +#define AAL5_MTU (1510+8) /* Default AAL5 Maximum Transmission Unit (and length of AAL5 buffers) */ | |
87 | +#define AAL5_BUFLEN (((AAL5_MTU + 47)/48)*48) /* Round up to n*48 bytes */ | |
88 | +#if 0 | |
89 | +# define DEBUG(format,args...) printk(format,##args) | |
90 | +#else | |
91 | +# define DEBUG(format,args...) | |
92 | +#endif | |
93 | +/*############ Types ##################################################*/ | |
94 | + | |
95 | +/* status flags shared between s/w and emulated h/w */ | |
96 | +typedef enum { | |
97 | + RX_EMPTY, /* No sk_buff present */ | |
98 | + RX_FULL, /* sk_buff present and awaiting data */ | |
99 | + RX_RECVD, /* sk_buff present and contains valid data */ | |
100 | +} myatmdd_rxstatus_e; | |
101 | + | |
102 | +/* status flags shared between s/w and emulated h/w */ | |
103 | +typedef enum { | |
104 | + TX_EMPTY, /* No sk_buff present */ | |
105 | + TX_FULL, /* sk_buff present and awaiting transmission */ | |
106 | + TX_SENT, /* sk_buff present and has been sent */ | |
107 | +} myatmdd_txstatus_e; | |
108 | + | |
109 | +typedef struct { | |
110 | + struct sk_buff **start; | |
111 | + struct sk_buff **end; | |
112 | + struct sk_buff **head; | |
113 | + struct sk_buff **tail; | |
114 | + | |
115 | + /* everything below this line emulates h/w */ | |
116 | + myatmdd_rxstatus_e *status; | |
117 | + struct sk_buff **hw_ptr; | |
118 | + int *pkt_len; | |
119 | + | |
120 | +} myatmdd_rxq_t; | |
121 | + | |
122 | +typedef struct { | |
123 | + struct sk_buff **start; | |
124 | + struct sk_buff **end; | |
125 | + struct sk_buff **head; | |
126 | + struct sk_buff **tail; | |
127 | + | |
128 | + /* everything below this line emulates h/w */ | |
129 | + myatmdd_txstatus_e *status; | |
130 | + struct sk_buff **hw_ptr; | |
131 | + int *pkt_len; | |
132 | + | |
133 | +} myatmdd_txq_t; | |
134 | + | |
135 | +typedef struct { | |
136 | +} myatmdd_devdata_t; | |
137 | + | |
138 | +typedef struct { | |
139 | + myatmdd_rxq_t rxqueue; | |
140 | + myatmdd_txq_t txqueue; | |
141 | +} myatmdd_vccdata_t; | |
142 | + | |
143 | +/*############ Module paramters #######################################*/ | |
144 | + | |
145 | +MODULE_AUTHOR("Alex Zeffertt, ajz@cambridgebroadband.com"); | |
146 | +MODULE_DESCRIPTION("Example ATM device driver (loopback)"); | |
147 | +#ifdef MODULE_LICENSE | |
148 | +MODULE_LICENSE("GPL"); | |
149 | +#endif | |
150 | +/*#################### Forward declarations ###########################*/ | |
151 | + | |
152 | +static void myatmdd_emulate_loopback_hardware(struct atm_vcc *vcc); | |
153 | + | |
154 | +static void myatmdd_free_tx_skb(struct sk_buff *skb); | |
155 | + | |
156 | +/* these functions will need modifying in a real ATM driver */ | |
157 | +static void myatmdd_rx_interrupt(struct atm_vcc *vcc); | |
158 | +static void myatmdd_tx_interrupt(struct atm_vcc *vcc); | |
159 | + | |
160 | +/* functions for manipulating circular bufs */ | |
161 | +static int myatmdd_init_rxq(myatmdd_rxq_t *queue, int size); | |
162 | +static int myatmdd_init_txq(myatmdd_txq_t *queue, int size); | |
163 | +static int myatmdd_release_rxq(myatmdd_rxq_t *queue); | |
164 | +static int myatmdd_release_txq(myatmdd_txq_t *queue); | |
165 | +static int myatmdd_txq_enqueue(myatmdd_txq_t *queue, struct sk_buff *skb); | |
166 | +static int myatmdd_rxq_enqueue(myatmdd_rxq_t *queue, struct sk_buff *skb /* empty buffer */); | |
167 | +static struct sk_buff *myatmdd_txq_dequeue(myatmdd_txq_t *queue); | |
168 | +static struct sk_buff *myatmdd_rxq_dequeue(myatmdd_rxq_t *queue, int *pkt_len); | |
169 | + | |
170 | +/* myatmdd_ops registered by ATM device */ | |
171 | +static int myatmdd_open(struct atm_vcc *vcc); | |
172 | +static void myatmdd_close(struct atm_vcc *vcc); | |
173 | +static int myatmdd_ioctl(struct atm_dev *dev, unsigned int cmd,void *arg); | |
174 | +static int myatmdd_setsockopt(struct atm_vcc *vcc,int level,int optname, void *optval,int optlen); | |
175 | +static int myatmdd_getsockopt(struct atm_vcc *vcc,int level,int optname, void *optval,int optlen); | |
176 | +static int myatmdd_send(struct atm_vcc *vcc,struct sk_buff *skb); | |
177 | +static int myatmdd_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs); | |
178 | +static int myatmdd_proc_read(struct atm_dev *dev,loff_t *pos,char *page); | |
179 | + | |
180 | +/* myatmdd_phy_ops registered by phy driver */ | |
181 | +static void myatmdd_phy_int(struct atm_dev *dev); | |
182 | +static int myatmdd_phy_start(struct atm_dev *dev); /* <-- This is the only thing exported by PHY driver */ | |
183 | +static int myatmdd_phy_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg); | |
184 | + | |
185 | +/*#################### Global scope variables #########################*/ | |
186 | + | |
187 | +/* operations registered by the atm device */ | |
188 | +static const struct atmdev_ops myatmdd_ops = | |
189 | +{ | |
190 | + open: myatmdd_open, | |
191 | + close: myatmdd_close, | |
192 | + ioctl: myatmdd_ioctl, | |
193 | + getsockopt: myatmdd_getsockopt, | |
194 | + setsockopt: myatmdd_setsockopt, | |
195 | + send: myatmdd_send, | |
196 | + change_qos: myatmdd_change_qos, | |
197 | + proc_read: myatmdd_proc_read, | |
198 | + owner: THIS_MODULE, | |
199 | +}; | |
200 | + | |
201 | +/* operations registered by the phy driver */ | |
202 | +static const struct atmphy_ops myatmdd_phy_ops = { | |
203 | + start: myatmdd_phy_start, | |
204 | + ioctl: myatmdd_phy_ioctl, | |
205 | + interrupt: myatmdd_phy_int, | |
206 | +}; | |
207 | + | |
208 | +struct atm_dev *myatmdd_dev; | |
209 | + | |
210 | +/*#################### Function definitions ###########################*/ | |
211 | + | |
212 | + | |
213 | +/* | |
214 | +######################################################### | |
215 | +# | |
216 | +# Function : myatmdd_rx_interrupt, and myatmdd_tx_interrupt | |
217 | +# | |
218 | +# Purpose : handle interrupt from hardware. In first | |
219 | +# case this means extract recvd buffers and pass | |
220 | +# it up protocol stack. In 2nd case this means | |
221 | +# free the sent buffers. | |
222 | +# | |
223 | +# Args : pointer to private data of the VCC concerned | |
224 | +# | |
225 | +# Returns : nowt | |
226 | +# | |
227 | +# Notes : | |
228 | +# | |
229 | +########################################################## | |
230 | +# Edit history: | |
231 | +# Who When What | |
232 | +# AJZ 10Apr03 Created | |
233 | +########################################################## | |
234 | +*/ | |
235 | + | |
236 | +static void myatmdd_rx_interrupt(struct atm_vcc *vcc) | |
237 | +{ | |
238 | + struct sk_buff *skb; | |
239 | + myatmdd_vccdata_t *priv = vcc->dev_data; | |
240 | + int pkt_len; | |
241 | + | |
242 | + DEBUG("%s\n", __FUNCTION__); | |
243 | + | |
244 | + while ((skb = myatmdd_rxq_dequeue(&priv->rxqueue, &pkt_len))) | |
245 | + { | |
246 | + struct sk_buff *newskb; | |
8ce7bd68 | 247 | + struct timeval stamp; |
8b732bb7 | 248 | + |
249 | + /* Get a new skb to replace the one just consumed */ | |
250 | + if (!(newskb = dev_alloc_skb(AAL5_BUFLEN))) | |
251 | + { | |
252 | + atomic_inc(&vcc->stats->rx_err); | |
253 | + printk(KERN_ERR KLOG_PREAMBLE "cannot receive packet - out of memory\n"); | |
254 | + /* put skb back in rx queue) */ | |
255 | + myatmdd_rxq_enqueue(&priv->rxqueue, skb); | |
256 | + return; | |
257 | + } | |
258 | + myatmdd_rxq_enqueue(&priv->rxqueue, newskb); | |
259 | + | |
260 | + if (!atm_charge (vcc, skb->truesize)) | |
261 | + { | |
262 | + /* Exceeded memory quota for this vcc | |
263 | + * NOTE: if atm_charge succeeds you must then push or accounting will screw up | |
264 | + */ | |
265 | + dev_kfree_skb(skb); | |
266 | + /* &vcc->stats->drop stats incremented in atm_charge */ | |
267 | + } | |
268 | + else | |
269 | + { | |
270 | + /* sk_buff passed all sanity checks! */ | |
271 | + | |
272 | + /* Add received length to socket buffer */ | |
273 | + skb_put(skb, pkt_len); | |
274 | + | |
275 | + /* update device stats */ | |
276 | + atomic_inc(&vcc->stats->rx); | |
277 | + | |
278 | + /* add timestamp for upper layers to use */ | |
8ce7bd68 PS |
279 | + do_gettimeofday(&stamp); |
280 | + skb_set_timestamp(skb, &stamp); | |
8b732bb7 | 281 | + |
282 | + /* Point socket buffer at the right VCC before giving to socket layer */ | |
283 | + ATM_SKB(skb)->vcc = vcc; | |
284 | + | |
285 | + /* push socket buffer up to ATM layer */ | |
286 | + vcc->push(vcc, skb); | |
287 | + } | |
288 | + } | |
289 | +} | |
290 | + | |
291 | +static void myatmdd_tx_interrupt(struct atm_vcc *vcc) | |
292 | +{ | |
293 | + struct sk_buff *skb; | |
294 | + myatmdd_vccdata_t *priv = vcc->dev_data; | |
295 | + | |
296 | + DEBUG("%s\n", __FUNCTION__); | |
297 | + | |
298 | + while ((skb = myatmdd_txq_dequeue(&priv->txqueue))) | |
299 | + { | |
300 | + // Update channel stats and free the memory | |
301 | + atomic_inc(&vcc->stats->tx); | |
302 | + myatmdd_free_tx_skb(skb); | |
303 | + } | |
304 | +} | |
305 | + | |
306 | +/* | |
307 | +######################################################### | |
308 | +# | |
309 | +# Function : myatmdd_emulate_loopback_hardware | |
310 | +# | |
311 | +# Purpose : emulate things normally done by hardware | |
312 | +# i.e. copying tx bufs to rx bufs (we're modelling | |
313 | +# a loopback system here), calling the tx done | |
314 | +# interrupt, and calling the rx done interrupt. | |
315 | +# | |
316 | +# Args : priv = data private to VCC | |
317 | +# | |
318 | +# Returns : nowt | |
319 | +# | |
320 | +# Notes : | |
321 | +# | |
322 | +########################################################## | |
323 | +# Edit history: | |
324 | +# Who When What | |
325 | +# AJZ 10Apr03 Created | |
326 | +########################################################## | |
327 | +*/ | |
328 | +static void myatmdd_emulate_loopback_hardware(struct atm_vcc *vcc) | |
329 | +{ | |
330 | + myatmdd_vccdata_t *priv = vcc->dev_data; | |
331 | + struct sk_buff **ptxskb; | |
332 | + struct sk_buff **prxskb; | |
333 | + | |
334 | + DEBUG("%s\n", __FUNCTION__); | |
335 | + | |
336 | + ptxskb = priv->txqueue.hw_ptr; | |
337 | + prxskb = priv->rxqueue.hw_ptr; | |
338 | + | |
339 | + /* Send each tx buff waiting to go */ | |
340 | + while (priv->txqueue.status[ptxskb - priv->txqueue.start] == TX_FULL) | |
341 | + { | |
342 | + struct sk_buff *txskb = *ptxskb; | |
343 | + struct sk_buff *rxskb = *prxskb; | |
344 | + int pkt_len = priv->txqueue.pkt_len[ptxskb - priv->txqueue.start]; | |
345 | + | |
346 | + /* Is there an rx buffer? */ | |
347 | + if (priv->rxqueue.status[prxskb - priv->rxqueue.start] == RX_FULL) | |
348 | + { | |
349 | + /* Yes - Is the length in range? */ | |
350 | + if (pkt_len <= AAL5_BUFLEN) | |
351 | + { | |
352 | + /* Yes - do the copy */ | |
353 | + memcpy(rxskb->data, txskb->data,pkt_len); | |
354 | + priv->rxqueue.pkt_len[prxskb - priv->rxqueue.start] = pkt_len; | |
355 | + | |
356 | + /* Indicate rx buffer recvd */ | |
357 | + priv->rxqueue.status[prxskb - priv->rxqueue.start] = RX_RECVD; | |
358 | + | |
359 | + /* increment and maybe wrap rx pointer */ | |
360 | + if (++prxskb == priv->rxqueue.end) | |
361 | + prxskb = priv->rxqueue.start; | |
362 | + priv->rxqueue.hw_ptr = prxskb; | |
363 | + } | |
364 | + else | |
365 | + { | |
366 | + /* No - then h/w cannot do a recv */ | |
367 | + printk(KERN_ERR KLOG_PREAMBLE "recvd frame too long - discarded\n"); | |
368 | + } | |
369 | + } | |
370 | + else | |
371 | + { | |
372 | + /* No - then h/w cannot do a recv */ | |
373 | + printk(KERN_ERR KLOG_PREAMBLE "no rx buffers available\n"); | |
374 | + } | |
375 | + | |
376 | + /* Indicate tx buffer sent */ | |
377 | + priv->txqueue.status[ptxskb - priv->txqueue.start] = TX_SENT; | |
378 | + | |
379 | + /* increment and maybe wrap tx pointer */ | |
380 | + if (++ptxskb == priv->txqueue.end) | |
381 | + ptxskb = priv->txqueue.start; | |
382 | + priv->txqueue.hw_ptr = ptxskb; | |
383 | + | |
384 | + /* Call tx ring interrupt handler */ | |
385 | + myatmdd_tx_interrupt(vcc); | |
386 | + | |
387 | + /* Call tx ring interrupt handler */ | |
388 | + myatmdd_rx_interrupt(vcc); | |
389 | + } | |
390 | +} | |
391 | + | |
392 | +/* | |
393 | +######################################################### | |
394 | +# | |
395 | +# Function : functions for manipulating circular buffs | |
396 | +# | |
397 | +# Purpose : | |
398 | +# | |
399 | +# Args : | |
400 | +# | |
401 | +# Returns : | |
402 | +# | |
403 | +# Notes : | |
404 | +# | |
405 | +########################################################## | |
406 | +# Edit history: | |
407 | +# Who When What | |
408 | +# AJZ 10Apr03 Created | |
409 | +########################################################## | |
410 | +*/ | |
411 | + | |
412 | +static int myatmdd_init_rxq(myatmdd_rxq_t *queue, int size) | |
413 | +{ | |
414 | + /* TODO - cope with kmalloc failure */ | |
415 | + struct sk_buff **pskb; | |
416 | + int i; | |
417 | + | |
418 | + DEBUG("%s\n", __FUNCTION__); | |
419 | + queue->hw_ptr = queue->head = queue->tail = | |
420 | + queue->start = kmalloc(size * sizeof(struct sk_buff *), GFP_KERNEL); | |
421 | + queue->end = &queue->start[size]; | |
422 | + for (pskb = queue->start; pskb < queue->end; pskb++) | |
423 | + *pskb = NULL; | |
424 | + | |
425 | + queue->status = kmalloc(size * sizeof(myatmdd_rxstatus_e),GFP_KERNEL); | |
426 | + for (i = 0; i < size; i++) | |
427 | + queue->status[i] = RX_EMPTY; | |
428 | + | |
429 | + queue->pkt_len = kmalloc(size * sizeof(int),GFP_KERNEL); | |
430 | + for (i = 0; i < size; i++) | |
431 | + queue->pkt_len[i] = 0; | |
432 | + | |
433 | + return 0; | |
434 | +} | |
435 | + | |
436 | +static int myatmdd_init_txq(myatmdd_txq_t *queue, int size) | |
437 | +{ | |
438 | + /* TODO - cope with kmalloc failure */ | |
439 | + struct sk_buff **pskb; | |
440 | + int i; | |
441 | + | |
442 | + DEBUG("%s\n", __FUNCTION__); | |
443 | + queue->hw_ptr = queue->head = queue->tail = | |
444 | + queue->start = kmalloc(size * sizeof(struct sk_buff *), GFP_KERNEL); | |
445 | + queue->end = &queue->start[size]; | |
446 | + for (pskb = queue->start; pskb < queue->end; pskb++) | |
447 | + *pskb = NULL; | |
448 | + | |
449 | + queue->status = kmalloc(size * sizeof(myatmdd_rxstatus_e),GFP_KERNEL); | |
450 | + for (i = 0; i < size; i++) | |
451 | + queue->status[i] = TX_EMPTY; | |
452 | + | |
453 | + queue->pkt_len = kmalloc(size * sizeof(int),GFP_KERNEL); | |
454 | + for (i = 0; i < size; i++) | |
455 | + queue->pkt_len[i] = 0; | |
456 | + | |
457 | + return 0; | |
458 | +} | |
459 | + | |
460 | +static int myatmdd_release_rxq(myatmdd_rxq_t *queue) | |
461 | +{ | |
462 | + struct sk_buff **pskb; | |
463 | + | |
464 | + DEBUG("%s\n", __FUNCTION__); | |
465 | + for (pskb = queue->start; pskb < queue->end; pskb++) | |
466 | + { | |
467 | + /* Is there an skb here */ | |
468 | + if (*pskb == NULL) | |
469 | + continue; /* No, so skip this entry in ring */ | |
470 | + | |
471 | + /* Yes - free it */ | |
472 | + dev_kfree_skb(*pskb); | |
473 | + } | |
474 | + kfree(queue->start); | |
475 | + kfree(queue->status); | |
476 | + kfree(queue->pkt_len); | |
477 | + | |
478 | + return 0; | |
479 | +} | |
480 | + | |
481 | +static int myatmdd_release_txq(myatmdd_txq_t *queue) | |
482 | +{ | |
483 | + struct sk_buff **pskb; | |
484 | + | |
485 | + DEBUG("%s\n", __FUNCTION__); | |
486 | + /* Scan through all TX bd's and cleanup */ | |
487 | + for (pskb = queue->start; pskb < queue->end; pskb++) | |
488 | + { | |
489 | + /* Is this buffer currently unused - i.e. no skb */ | |
490 | + if (*pskb == NULL) | |
491 | + continue; /* Yes, so ignore it */ | |
492 | + | |
493 | + /* If we reach here, we have found a socket buffer that | |
494 | + * exists in the TX ring and is waiting to be released. | |
495 | + */ | |
496 | + printk(KERN_WARNING KLOG_PREAMBLE "discarding unsent tx sk_buff\n"); | |
497 | + atomic_inc(&ATM_SKB(*pskb)->vcc->stats->tx_err); | |
498 | + myatmdd_free_tx_skb(*pskb); | |
499 | + } | |
500 | + kfree(queue->start); | |
501 | + kfree(queue->status); | |
502 | + kfree(queue->pkt_len); | |
503 | + | |
504 | + return 0; | |
505 | +} | |
506 | + | |
507 | +/* returns non-zero for "out of space" */ | |
508 | +static int myatmdd_txq_enqueue(myatmdd_txq_t *queue, struct sk_buff *skb) | |
509 | +{ | |
510 | + /* increment head and wrap */ | |
511 | + struct sk_buff **newhead = queue->head + 1; | |
512 | + if (newhead == queue->end) | |
513 | + newhead = queue->start; | |
514 | + | |
515 | + DEBUG("%s\n", __FUNCTION__); | |
516 | + | |
517 | + /* abort if tx ring full */ | |
518 | + if (newhead == queue->tail) | |
519 | + return -1; | |
520 | + | |
521 | + /* all is okay if we're here */ | |
522 | + *queue->head = skb; | |
523 | + /* Tell hardware there's a buffer to send */ | |
524 | + queue->status[queue->head - queue->start] = TX_FULL; | |
525 | + queue->pkt_len[queue->head - queue->start] = skb->len; | |
526 | + queue->head = newhead; | |
527 | + return 0; | |
528 | +} | |
529 | + | |
530 | +/* returns non-zero for "out of space" */ | |
531 | +static int myatmdd_rxq_enqueue(myatmdd_rxq_t *queue, struct sk_buff *skb /* empty buffer */) | |
532 | +{ | |
533 | + /* increment head and wrap */ | |
534 | + struct sk_buff **newhead = queue->head + 1; | |
535 | + if (newhead == queue->end) | |
536 | + newhead = queue->start; | |
537 | + | |
538 | + DEBUG("%s\n", __FUNCTION__); | |
539 | + | |
540 | + /* abort if rx ring full */ | |
541 | + if (newhead == queue->tail) | |
542 | + return -1; | |
543 | + | |
544 | + /* all is okay if we're here */ | |
545 | + *queue->head = skb; | |
546 | + /* Tell hardware there's a buffer to send */ | |
547 | + queue->status[queue->head - queue->start] = RX_FULL; | |
548 | + queue->head = newhead; | |
549 | + return 0; | |
550 | +} | |
551 | + | |
552 | +static struct sk_buff *myatmdd_txq_dequeue(myatmdd_txq_t *queue) | |
553 | +{ | |
554 | + DEBUG("%s\n", __FUNCTION__); | |
555 | + if (queue->tail != queue->head && queue->status[queue->tail - queue->start] == TX_SENT) | |
556 | + { | |
557 | + struct sk_buff *skb = *queue->tail; | |
558 | + | |
559 | + /* increment tail and wrap */ | |
560 | + struct sk_buff **newtail = queue->tail + 1; | |
561 | + if (newtail == queue->end) | |
562 | + newtail = queue->start; | |
563 | + *queue->tail = NULL; | |
564 | + queue->status[queue->tail - queue->start] = TX_EMPTY; | |
565 | + queue->tail = newtail; | |
566 | + return skb; | |
567 | + } | |
568 | + return NULL; | |
569 | +} | |
570 | + | |
571 | +/* returns NULL for "no new recvd frames" */ | |
572 | +static struct sk_buff *myatmdd_rxq_dequeue(myatmdd_rxq_t *queue, int *pkt_len) | |
573 | +{ | |
574 | + DEBUG("%s\n", __FUNCTION__); | |
575 | + if (queue->tail != queue->head && queue->status[queue->tail - queue->start] == RX_RECVD) | |
576 | + { | |
577 | + struct sk_buff *skb = *queue->tail; | |
578 | + | |
579 | + /* increment tail and wrap */ | |
580 | + struct sk_buff **newtail = queue->tail + 1; | |
581 | + if (newtail == queue->end) | |
582 | + newtail = queue->start; | |
583 | + *queue->tail = NULL; | |
584 | + queue->status[queue->tail - queue->start] = RX_EMPTY; | |
585 | + *pkt_len = queue->pkt_len[queue->tail - queue->start]; | |
586 | + queue->tail = newtail; | |
587 | + return skb; | |
588 | + } | |
589 | + return NULL; | |
590 | +} | |
591 | + | |
592 | +/* | |
593 | +######################################################### | |
594 | +# | |
595 | +# Functions : Implementations of function ptrs in | |
596 | +# myatmdd_phy_ops. This is the phy driver | |
597 | +# start: myatmdd_phy_start, | |
598 | +# ioctl: myatmdd_phy_ioctl, | |
599 | +# interrupt: myatmdd_phy_int, | |
600 | +# | |
601 | +# Purpose : See ATM device driver interface v0.1 | |
602 | +# | |
603 | +# Notes : Conforming to Linux ATM device driver i/f | |
604 | +# interface. Draft version 0.1 | |
605 | +# | |
606 | +# Designed to work with multiple devices | |
607 | +########################################################## | |
608 | +# Edit history: | |
609 | +# Who When What | |
610 | +# AJZ 10Apr03 Created | |
611 | +########################################################## | |
612 | +*/ | |
613 | +static int myatmdd_phy_start(struct atm_dev *dev) | |
614 | +{ | |
615 | + /* Provide ATM driver with a pointer via which it | |
616 | + * may invoke PHY driver's IOCTL or interrupt | |
617 | + * handlers. | |
618 | + */ | |
619 | + dev->phy = &myatmdd_phy_ops; | |
620 | + | |
621 | + /* If required allocate phy private data and save | |
622 | + * pointer in dev->phy_data; | |
623 | + */ | |
624 | + | |
625 | + /* TODO Initialise PHY hardware... */ | |
626 | + | |
627 | + return 0; | |
628 | +} | |
629 | + | |
630 | +/* Should be called by SAR driver when it needs to handle an interrupt | |
631 | + * triggered by PHY. | |
632 | + */ | |
633 | +static void myatmdd_phy_int(struct atm_dev *dev) | |
634 | +{ | |
635 | + /* Handle interrupt triggered by PHY */ | |
636 | +} | |
637 | + | |
638 | +/* Gets called by SAR driver IOCTL handler for IOCTLS it doesn't recognise */ | |
639 | +static int myatmdd_phy_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) | |
640 | +{ | |
641 | + switch (cmd) | |
642 | + { | |
643 | +// case SONET_GETSTATZ: | |
644 | + default: | |
645 | + return -EINVAL; | |
646 | + } | |
647 | +} | |
648 | + | |
649 | +/* | |
650 | +######################################################### | |
651 | +# | |
652 | +# Function : myatmdd_free_tx_skb | |
653 | +# | |
654 | +# Purpose : frees an sk_buff. | |
655 | +# | |
656 | +# Args : skb=pointer to socket buffer | |
657 | +# | |
658 | +# Notes : Tries to use the upper layer pop() function | |
659 | +# but uses dev_kfree_skb() if this doesn't exist | |
660 | +########################################################## | |
661 | +# Edit history: | |
662 | +# Who When What | |
663 | +# AJZ 10Apr03 Created | |
664 | +########################################################## | |
665 | +*/ | |
666 | +static void myatmdd_free_tx_skb(struct sk_buff *skb) | |
667 | +{ | |
668 | + struct atm_vcc *vcc; | |
669 | + | |
670 | + DEBUG("%s\n", __FUNCTION__); | |
671 | + | |
672 | + /* See if we can use the VCC pop function */ | |
673 | + if (((vcc = ATM_SKB(skb)->vcc) != NULL) && (vcc->pop != NULL)) | |
674 | + { | |
675 | + /* Yes, so use ATM socket layer pop function */ | |
676 | + vcc->pop(vcc, skb); | |
677 | + } | |
678 | + else | |
679 | + { | |
680 | + printk(KERN_WARNING KLOG_PREAMBLE "unable to call skb free function\n"); | |
681 | + /* No, so free socket buffer */ | |
682 | + dev_kfree_skb(skb); | |
683 | + } | |
684 | +} | |
685 | + | |
686 | +/* | |
687 | +######################################################### | |
688 | +# | |
689 | +# Functions : Implementations of function ptrs in | |
690 | +# myatmdd_ops. | |
691 | +# myatmdd_open(), | |
692 | +# myatmdd_close(), | |
693 | +# myatmdd_ioctl(), | |
694 | +# myatmdd_getsockopt(), | |
695 | +# myatmdd_setsockopt(), | |
696 | +# myatmdd_send(), | |
697 | +# myatmdd_sg_send(), | |
698 | +# myatmdd_change_qos(), | |
699 | +# myatmdd_proc_read() | |
700 | +# | |
701 | +# | |
702 | +# Purpose : See ATM device driver interface v0.1 | |
703 | +# | |
704 | +# Notes : Conforming to Linux ATM device driver i/f | |
705 | +# interface. Draft version 0.1 | |
706 | +# | |
707 | +# Designed to work with multiple devices | |
708 | +########################################################## | |
709 | +# Edit history: | |
710 | +# Who When What | |
711 | +# AJZ 10Apr03 Created | |
712 | +########################################################## | |
713 | +*/ | |
714 | +static int myatmdd_open(struct atm_vcc *vcc) | |
715 | +{ | |
716 | + myatmdd_vccdata_t *priv; | |
717 | + int i; | |
718 | + | |
719 | + DEBUG("%s\n", __FUNCTION__); | |
720 | + | |
721 | + /* Make sure we are opening a AAL0 or AAL5 connection */ | |
722 | + if ((vcc->qos.aal != ATM_AAL5) && (vcc->qos.aal != ATM_AAL0)) | |
723 | + { | |
724 | + printk(KERN_WARNING KLOG_PREAMBLE "invalid AAL\n"); | |
725 | + return -EINVAL; | |
726 | + } | |
727 | + | |
728 | + /* Address is in use */ | |
729 | + set_bit(ATM_VF_ADDR, &vcc->flags); | |
730 | + | |
731 | + /* Allocate some vcc-private memory */ | |
732 | + vcc->dev_data = kmalloc(sizeof(myatmdd_vccdata_t), GFP_KERNEL); | |
733 | + if (vcc->dev_data == NULL) | |
734 | + return -ENOMEM; | |
735 | + priv = vcc->dev_data; | |
736 | + | |
737 | + /* Setup the hardware for new VC... */ | |
738 | + | |
739 | + /* Do not allow half open VCs - otherwise the example driver will not be able | |
740 | + * to loop back frames sent ! | |
741 | + */ | |
742 | + if (vcc->qos.rxtp.traffic_class == ATM_NONE || vcc->qos.txtp.traffic_class == ATM_NONE) | |
743 | + { | |
744 | + kfree(vcc->dev_data); | |
745 | + return -EPERM; | |
746 | + } | |
747 | + | |
748 | + /* Create rx/tx queues for this VC */ | |
749 | + myatmdd_init_txq(&priv->txqueue, TXQ_SZ); | |
750 | + myatmdd_init_rxq(&priv->rxqueue, RXQ_SZ); | |
751 | + | |
752 | + /* Fill rx queue with empty skbuffs */ | |
753 | + for (i = 0 ; i < RXQ_SZ - 1; i++) | |
754 | + { | |
755 | + struct sk_buff *skb = dev_alloc_skb(AAL5_BUFLEN); | |
756 | + myatmdd_rxq_enqueue(&priv->rxqueue,skb); | |
757 | + } | |
758 | + | |
759 | + /* Connection is now ready to receive data */ | |
760 | + set_bit(ATM_VF_READY, &vcc->flags); | |
761 | + | |
762 | + return 0; | |
763 | +} | |
764 | + | |
765 | +static void myatmdd_close(struct atm_vcc *vcc) | |
766 | +{ | |
767 | + myatmdd_vccdata_t *priv = vcc->dev_data; | |
768 | + | |
769 | + DEBUG("%s\n", __FUNCTION__); | |
770 | + | |
771 | + /* Indicate channel closed */ | |
772 | + clear_bit(ATM_VF_READY, &vcc->flags); | |
773 | + | |
774 | + /* TODO Uninitialise the hardware for this VC... */ | |
775 | + | |
776 | + /* empty the rx and tx queues */ | |
777 | + myatmdd_release_txq(&priv->txqueue); | |
778 | + myatmdd_release_rxq(&priv->rxqueue); | |
779 | + | |
780 | + /* Free the vcc-private memory */ | |
781 | + kfree(vcc->dev_data); | |
782 | +} | |
783 | + | |
784 | +static int myatmdd_ioctl(struct atm_dev *dev, unsigned int cmd,void *arg) | |
785 | +{ | |
786 | + /* myatmdd does not currently have an ioctl interface so pass ioctl onto PHY */ | |
787 | + if (dev->phy && dev->phy->ioctl) { | |
788 | + return dev->phy->ioctl(dev, cmd, arg); | |
789 | + } | |
790 | + return -EINVAL; | |
791 | +} | |
792 | + | |
793 | +static int myatmdd_getsockopt(struct atm_vcc *vcc,int level,int optname, void *optval,int optlen) | |
794 | +{ | |
795 | + return -EINVAL; | |
796 | +} | |
797 | + | |
798 | +static int myatmdd_setsockopt(struct atm_vcc *vcc,int level,int optname, void *optval,int optlen) | |
799 | +{ | |
800 | + return -EINVAL; | |
801 | +} | |
802 | + | |
803 | +/* Note may be called in either process or interrupt context! */ | |
804 | +static int myatmdd_send(struct atm_vcc *vcc,struct sk_buff *skb) | |
805 | +{ | |
806 | + myatmdd_vccdata_t *priv = vcc->dev_data; | |
807 | + | |
808 | + DEBUG("%s\n", __FUNCTION__); | |
809 | + | |
810 | + /* Assign VCC to socket buffer | |
811 | + * Note: this must be done before attempting to call | |
812 | + * myatmdd_free_tx_skb() as this may use ATM_SKB(skb)->vcc->pop() | |
813 | + */ | |
814 | + ATM_SKB(skb)->vcc = vcc; | |
815 | + | |
816 | + /* Setup hardware to send and arrange callback of myatmdd_send_complete... */ | |
817 | + | |
818 | + /* In this example ATM device driver all VCs are looped back. | |
819 | + * So copy to the rxq and emulate an rx interrupt | |
820 | + */ | |
821 | + | |
822 | + /* Can we accept another skb to send ? */ | |
823 | + if (myatmdd_txq_enqueue(&priv->txqueue, skb)) | |
824 | + { | |
825 | + /* No - free socket buffer */ | |
826 | + myatmdd_free_tx_skb(skb); | |
827 | + | |
828 | + /* Update tx channel stats */ | |
829 | + atomic_inc(&vcc->stats->tx_err); | |
830 | + | |
831 | + /* Tell protocol layer to back off */ | |
832 | + return(-EBUSY); | |
833 | + } | |
834 | + | |
835 | + /* This is the bit which copies the tx ring to the rx ring, | |
836 | + * and triggers emulated rx and tx interrupts | |
837 | + */ | |
838 | + myatmdd_emulate_loopback_hardware(vcc); | |
839 | + | |
840 | + return 0; | |
841 | +} | |
842 | + | |
843 | +static int myatmdd_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs) | |
844 | +{ | |
845 | + return 0; | |
846 | +} | |
847 | + | |
848 | +static int myatmdd_proc_read(struct atm_dev *dev,loff_t *pos,char *page) | |
849 | +{ | |
850 | + int left = (int) *pos; | |
851 | + | |
852 | + if (!left--) | |
853 | + return sprintf(page, "1st line of stats\n"); | |
854 | + if (!left--) | |
855 | + return sprintf(page, "2nd line of stats\n"); | |
856 | + if (!left--) | |
857 | + return sprintf(page, "3rd line of stats\n"); | |
858 | + | |
859 | + return 0; | |
860 | +} | |
861 | + | |
862 | +/* | |
863 | +######################################################### | |
864 | +# | |
865 | +# Function : myatmdd_init | |
866 | +# | |
867 | +# Purpose : init the module, init and register the ATM device | |
868 | +# | |
869 | +# Args : none | |
870 | +# | |
871 | +# Returns : return code | |
872 | +# | |
873 | +# Notes : | |
874 | +# | |
875 | +########################################################## | |
876 | +# Edit history: | |
877 | +# Who When What | |
878 | +# AJZ 10Apr03 Created | |
879 | +########################################################## | |
880 | +*/ | |
881 | +int __init myatmdd_init(void) | |
882 | +{ | |
883 | + myatmdd_devdata_t *priv = kmalloc(sizeof(myatmdd_devdata_t),GFP_KERNEL); | |
884 | + | |
885 | + if (priv == NULL) | |
886 | + return -ENOMEM; | |
887 | + | |
888 | + /* Register the new device */ | |
889 | + myatmdd_dev = atm_dev_register(MYATMDD,&myatmdd_ops,-1,NULL); | |
890 | + | |
891 | + /* Were we able to register this device? */ | |
892 | + if (myatmdd_dev == NULL) | |
893 | + { | |
894 | + printk(KERN_ERR KLOG_PREAMBLE "failed to register CPM ATM device\n"); | |
895 | + return -EPERM; | |
896 | + } | |
897 | + | |
898 | + /* Save pointer to device private data */ | |
899 | + myatmdd_dev->dev_data = priv; | |
900 | + | |
901 | + /* Initialise device parameters */ | |
902 | + myatmdd_dev->ci_range.vpi_bits = MYATMDD_VPI_BITS; | |
903 | + myatmdd_dev->ci_range.vci_bits = MYATMDD_VCI_BITS; | |
904 | + myatmdd_dev->link_rate = MYATMDD_PCR; | |
905 | + | |
906 | + /* Set up phy device */ | |
907 | + myatmdd_phy_start(myatmdd_dev); | |
908 | + | |
909 | + /* TODO Initialise SAR hardware... */ | |
910 | + | |
911 | + /* Console output */ | |
912 | + printk(KERN_INFO KLOG_PREAMBLE "Initialised\n"); | |
913 | + | |
914 | + return 0; | |
915 | +} | |
916 | + | |
917 | +/* NOTE: | |
918 | + * module_init() is called by insmod, if built as module, | |
919 | + * or by do_initcalls(), if built as a resident driver. | |
920 | + */ | |
921 | +module_init(myatmdd_init); | |
922 | + | |
923 | +/* | |
924 | +######################################################### | |
925 | +# | |
926 | +# Function : myatmdd_exit | |
927 | +# | |
928 | +# Purpose : delete module, uninit and dereg ATM device | |
929 | +# | |
930 | +# Args : none | |
931 | +# | |
932 | +# Returns : none | |
933 | +# | |
934 | +# Notes : | |
935 | +# | |
936 | +########################################################## | |
937 | +# Edit history: | |
938 | +# Who When What | |
939 | +# AJZ 10Apr03 Created | |
940 | +########################################################## | |
941 | +*/ | |
942 | + | |
943 | +#ifdef MODULE | |
944 | +static void __exit myatmdd_exit(void) | |
945 | +{ | |
946 | + /* Disable SAR hardware... */ | |
947 | + | |
948 | + /* Console output */ | |
949 | + printk(KERN_ERR KLOG_PREAMBLE "Uninitialised\n"); | |
950 | + kfree(myatmdd_dev->dev_data); | |
951 | + atm_dev_deregister(myatmdd_dev); | |
952 | +} | |
953 | +module_exit(myatmdd_exit); | |
954 | + | |
955 | +#endif /* MODULE */ |