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