1 diff -urN linux-5.9/drivers.org/atm/atmdd.c linux-5.9/drivers/atm/atmdd.c
2 --- linux-5.9/drivers.org/atm/atmdd.c 1970-01-01 01:00:00.000000000 +0100
3 +++ linux-5.9/drivers/atm/atmdd.c 2020-11-03 13:31:48.120071319 +0100
6 +#######################################################################
9 +# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com
11 +# This program is free software; you can redistribute it and/or
12 +# modify it under the terms of the GNU General Public License as
13 +# published by the Free Software Foundation; either version 2 of
14 +# the License, or (at your option) any later version.
16 +# This program is distributed in the hope that it will be useful,
17 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
18 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 +# GNU General Public License for more details.
21 +# You should have received a copy of the GNU General Public License
22 +# along with this program; if not, write to the Free Software
23 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 +#######################################################################
28 +# This is an example atm driver. It does not require any actual ATM
29 +# hardware. It supports AAL5 and AAL0. frames are merely looped back
30 +# to the sender on the same VC they were sent.
32 +#######################################################################
35 +/*############ Includes ###############################################*/
37 +#include <linux/module.h>
38 +#include <linux/kernel.h>
39 +#include <linux/errno.h>
40 +#include <linux/atm.h>
41 +#include <linux/atmdev.h>
42 +#include <linux/skbuff.h>
43 +#include <linux/init.h>
44 +#include <linux/netdevice.h>
45 +#include <linux/sched.h> /* for xtime */
47 +/*############ Defines ################################################*/
49 +#define MYATMDD "atmdd"
50 +#define KLOG_PREAMBLE MYATMDD ": "
51 +#define MYATMDD_VPI_BITS 1 /* Allow ?.1.? but not ?.2.? */
52 +#define MYATMDD_VCI_BITS 11 /* Allow ?.?.2047 but not ?.?.2048 */
53 +#define MYATMDD_PCR 100000
56 +#define AAL5_MTU (1510+8) /* Default AAL5 Maximum Transmission Unit (and length of AAL5 buffers) */
57 +#define AAL5_BUFLEN (((AAL5_MTU + 47)/48)*48) /* Round up to n*48 bytes */
59 +# define DEBUG(format,args...) printk(format,##args)
61 +# define DEBUG(format,args...)
63 +/*############ Types ##################################################*/
65 +/* status flags shared between s/w and emulated h/w */
67 + RX_EMPTY, /* No sk_buff present */
68 + RX_FULL, /* sk_buff present and awaiting data */
69 + RX_RECVD, /* sk_buff present and contains valid data */
70 +} myatmdd_rxstatus_e;
72 +/* status flags shared between s/w and emulated h/w */
74 + TX_EMPTY, /* No sk_buff present */
75 + TX_FULL, /* sk_buff present and awaiting transmission */
76 + TX_SENT, /* sk_buff present and has been sent */
77 +} myatmdd_txstatus_e;
80 + struct sk_buff **start;
81 + struct sk_buff **end;
82 + struct sk_buff **head;
83 + struct sk_buff **tail;
85 + /* everything below this line emulates h/w */
86 + myatmdd_rxstatus_e *status;
87 + struct sk_buff **hw_ptr;
93 + struct sk_buff **start;
94 + struct sk_buff **end;
95 + struct sk_buff **head;
96 + struct sk_buff **tail;
98 + /* everything below this line emulates h/w */
99 + myatmdd_txstatus_e *status;
100 + struct sk_buff **hw_ptr;
106 +} myatmdd_devdata_t;
109 + myatmdd_rxq_t rxqueue;
110 + myatmdd_txq_t txqueue;
111 +} myatmdd_vccdata_t;
113 +/*############ Module paramters #######################################*/
115 +MODULE_AUTHOR("Alex Zeffertt, ajz@cambridgebroadband.com");
116 +MODULE_DESCRIPTION("Example ATM device driver (loopback)");
117 +#ifdef MODULE_LICENSE
118 +MODULE_LICENSE("GPL");
120 +/*#################### Forward declarations ###########################*/
122 +static void myatmdd_emulate_loopback_hardware(struct atm_vcc *vcc);
124 +static void myatmdd_free_tx_skb(struct sk_buff *skb);
126 +/* these functions will need modifying in a real ATM driver */
127 +static void myatmdd_rx_interrupt(struct atm_vcc *vcc);
128 +static void myatmdd_tx_interrupt(struct atm_vcc *vcc);
130 +/* functions for manipulating circular bufs */
131 +static int myatmdd_init_rxq(myatmdd_rxq_t *queue, int size);
132 +static int myatmdd_init_txq(myatmdd_txq_t *queue, int size);
133 +static int myatmdd_release_rxq(myatmdd_rxq_t *queue);
134 +static int myatmdd_release_txq(myatmdd_txq_t *queue);
135 +static int myatmdd_txq_enqueue(myatmdd_txq_t *queue, struct sk_buff *skb);
136 +static int myatmdd_rxq_enqueue(myatmdd_rxq_t *queue, struct sk_buff *skb /* empty buffer */);
137 +static struct sk_buff *myatmdd_txq_dequeue(myatmdd_txq_t *queue);
138 +static struct sk_buff *myatmdd_rxq_dequeue(myatmdd_rxq_t *queue, int *pkt_len);
140 +/* myatmdd_ops registered by ATM device */
141 +static int myatmdd_open(struct atm_vcc *vcc);
142 +static void myatmdd_close(struct atm_vcc *vcc);
143 +static int myatmdd_ioctl(struct atm_dev *dev, unsigned int cmd,void *arg);
146 +static int myatmdd_send(struct atm_vcc *vcc,struct sk_buff *skb);
147 +static int myatmdd_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs);
148 +static int myatmdd_proc_read(struct atm_dev *dev,loff_t *pos,char *page);
150 +/* myatmdd_phy_ops registered by phy driver */
151 +static void myatmdd_phy_int(struct atm_dev *dev);
152 +static int myatmdd_phy_start(struct atm_dev *dev); /* <-- This is the only thing exported by PHY driver */
153 +static int myatmdd_phy_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg);
155 +/*#################### Global scope variables #########################*/
157 +/* operations registered by the atm device */
158 +static const struct atmdev_ops myatmdd_ops =
160 + open: myatmdd_open,
161 + close: myatmdd_close,
162 + ioctl: myatmdd_ioctl,
165 + send: myatmdd_send,
166 + change_qos: myatmdd_change_qos,
167 + proc_read: myatmdd_proc_read,
168 + owner: THIS_MODULE,
171 +/* operations registered by the phy driver */
172 +static const struct atmphy_ops myatmdd_phy_ops = {
173 + start: myatmdd_phy_start,
174 + ioctl: myatmdd_phy_ioctl,
175 + interrupt: myatmdd_phy_int,
178 +struct atm_dev *myatmdd_dev;
180 +/*#################### Function definitions ###########################*/
184 +#########################################################
186 +# Function : myatmdd_rx_interrupt, and myatmdd_tx_interrupt
188 +# Purpose : handle interrupt from hardware. In first
189 +# case this means extract recvd buffers and pass
190 +# it up protocol stack. In 2nd case this means
191 +# free the sent buffers.
193 +# Args : pointer to private data of the VCC concerned
199 +##########################################################
202 +# AJZ 10Apr03 Created
203 +##########################################################
206 +static void myatmdd_rx_interrupt(struct atm_vcc *vcc)
208 + struct sk_buff *skb;
209 + myatmdd_vccdata_t *priv = vcc->dev_data;
212 + DEBUG("%s\n", __FUNCTION__);
214 + while ((skb = myatmdd_rxq_dequeue(&priv->rxqueue, &pkt_len)))
216 + struct sk_buff *newskb;
218 + /* Get a new skb to replace the one just consumed */
219 + if (!(newskb = dev_alloc_skb(AAL5_BUFLEN)))
221 + atomic_inc(&vcc->stats->rx_err);
222 + printk(KERN_ERR KLOG_PREAMBLE "cannot receive packet - out of memory\n");
223 + /* put skb back in rx queue) */
224 + myatmdd_rxq_enqueue(&priv->rxqueue, skb);
227 + myatmdd_rxq_enqueue(&priv->rxqueue, newskb);
229 + if (!atm_charge (vcc, skb->truesize))
231 + /* Exceeded memory quota for this vcc
232 + * NOTE: if atm_charge succeeds you must then push or accounting will screw up
234 + dev_kfree_skb(skb);
235 + /* &vcc->stats->drop stats incremented in atm_charge */
239 + /* sk_buff passed all sanity checks! */
241 + /* Add received length to socket buffer */
242 + skb_put(skb, pkt_len);
244 + /* update device stats */
245 + atomic_inc(&vcc->stats->rx);
247 + /* add timestamp for upper layers to use */
248 + ktime_t kt = ktime_get_real();
251 + /* Point socket buffer at the right VCC before giving to socket layer */
252 + ATM_SKB(skb)->vcc = vcc;
254 + /* push socket buffer up to ATM layer */
255 + vcc->push(vcc, skb);
260 +static void myatmdd_tx_interrupt(struct atm_vcc *vcc)
262 + struct sk_buff *skb;
263 + myatmdd_vccdata_t *priv = vcc->dev_data;
265 + DEBUG("%s\n", __FUNCTION__);
267 + while ((skb = myatmdd_txq_dequeue(&priv->txqueue)))
269 + // Update channel stats and free the memory
270 + atomic_inc(&vcc->stats->tx);
271 + myatmdd_free_tx_skb(skb);
276 +#########################################################
278 +# Function : myatmdd_emulate_loopback_hardware
280 +# Purpose : emulate things normally done by hardware
281 +# i.e. copying tx bufs to rx bufs (we're modelling
282 +# a loopback system here), calling the tx done
283 +# interrupt, and calling the rx done interrupt.
285 +# Args : priv = data private to VCC
291 +##########################################################
294 +# AJZ 10Apr03 Created
295 +##########################################################
297 +static void myatmdd_emulate_loopback_hardware(struct atm_vcc *vcc)
299 + myatmdd_vccdata_t *priv = vcc->dev_data;
300 + struct sk_buff **ptxskb;
301 + struct sk_buff **prxskb;
303 + DEBUG("%s\n", __FUNCTION__);
305 + ptxskb = priv->txqueue.hw_ptr;
306 + prxskb = priv->rxqueue.hw_ptr;
308 + /* Send each tx buff waiting to go */
309 + while (priv->txqueue.status[ptxskb - priv->txqueue.start] == TX_FULL)
311 + struct sk_buff *txskb = *ptxskb;
312 + struct sk_buff *rxskb = *prxskb;
313 + int pkt_len = priv->txqueue.pkt_len[ptxskb - priv->txqueue.start];
315 + /* Is there an rx buffer? */
316 + if (priv->rxqueue.status[prxskb - priv->rxqueue.start] == RX_FULL)
318 + /* Yes - Is the length in range? */
319 + if (pkt_len <= AAL5_BUFLEN)
321 + /* Yes - do the copy */
322 + memcpy(rxskb->data, txskb->data,pkt_len);
323 + priv->rxqueue.pkt_len[prxskb - priv->rxqueue.start] = pkt_len;
325 + /* Indicate rx buffer recvd */
326 + priv->rxqueue.status[prxskb - priv->rxqueue.start] = RX_RECVD;
328 + /* increment and maybe wrap rx pointer */
329 + if (++prxskb == priv->rxqueue.end)
330 + prxskb = priv->rxqueue.start;
331 + priv->rxqueue.hw_ptr = prxskb;
335 + /* No - then h/w cannot do a recv */
336 + printk(KERN_ERR KLOG_PREAMBLE "recvd frame too long - discarded\n");
341 + /* No - then h/w cannot do a recv */
342 + printk(KERN_ERR KLOG_PREAMBLE "no rx buffers available\n");
345 + /* Indicate tx buffer sent */
346 + priv->txqueue.status[ptxskb - priv->txqueue.start] = TX_SENT;
348 + /* increment and maybe wrap tx pointer */
349 + if (++ptxskb == priv->txqueue.end)
350 + ptxskb = priv->txqueue.start;
351 + priv->txqueue.hw_ptr = ptxskb;
353 + /* Call tx ring interrupt handler */
354 + myatmdd_tx_interrupt(vcc);
356 + /* Call tx ring interrupt handler */
357 + myatmdd_rx_interrupt(vcc);
362 +#########################################################
364 +# Function : functions for manipulating circular buffs
374 +##########################################################
377 +# AJZ 10Apr03 Created
378 +##########################################################
381 +static int myatmdd_init_rxq(myatmdd_rxq_t *queue, int size)
383 + /* TODO - cope with kmalloc failure */
384 + struct sk_buff **pskb;
387 + DEBUG("%s\n", __FUNCTION__);
388 + queue->hw_ptr = queue->head = queue->tail =
389 + queue->start = kmalloc(size * sizeof(struct sk_buff *), GFP_KERNEL);
390 + queue->end = &queue->start[size];
391 + for (pskb = queue->start; pskb < queue->end; pskb++)
394 + queue->status = kmalloc(size * sizeof(myatmdd_rxstatus_e),GFP_KERNEL);
395 + for (i = 0; i < size; i++)
396 + queue->status[i] = RX_EMPTY;
398 + queue->pkt_len = kmalloc(size * sizeof(int),GFP_KERNEL);
399 + for (i = 0; i < size; i++)
400 + queue->pkt_len[i] = 0;
405 +static int myatmdd_init_txq(myatmdd_txq_t *queue, int size)
407 + /* TODO - cope with kmalloc failure */
408 + struct sk_buff **pskb;
411 + DEBUG("%s\n", __FUNCTION__);
412 + queue->hw_ptr = queue->head = queue->tail =
413 + queue->start = kmalloc(size * sizeof(struct sk_buff *), GFP_KERNEL);
414 + queue->end = &queue->start[size];
415 + for (pskb = queue->start; pskb < queue->end; pskb++)
418 + queue->status = kmalloc(size * sizeof(myatmdd_rxstatus_e),GFP_KERNEL);
419 + for (i = 0; i < size; i++)
420 + queue->status[i] = TX_EMPTY;
422 + queue->pkt_len = kmalloc(size * sizeof(int),GFP_KERNEL);
423 + for (i = 0; i < size; i++)
424 + queue->pkt_len[i] = 0;
429 +static int myatmdd_release_rxq(myatmdd_rxq_t *queue)
431 + struct sk_buff **pskb;
433 + DEBUG("%s\n", __FUNCTION__);
434 + for (pskb = queue->start; pskb < queue->end; pskb++)
436 + /* Is there an skb here */
438 + continue; /* No, so skip this entry in ring */
440 + /* Yes - free it */
441 + dev_kfree_skb(*pskb);
443 + kfree(queue->start);
444 + kfree(queue->status);
445 + kfree(queue->pkt_len);
450 +static int myatmdd_release_txq(myatmdd_txq_t *queue)
452 + struct sk_buff **pskb;
454 + DEBUG("%s\n", __FUNCTION__);
455 + /* Scan through all TX bd's and cleanup */
456 + for (pskb = queue->start; pskb < queue->end; pskb++)
458 + /* Is this buffer currently unused - i.e. no skb */
460 + continue; /* Yes, so ignore it */
462 + /* If we reach here, we have found a socket buffer that
463 + * exists in the TX ring and is waiting to be released.
465 + printk(KERN_WARNING KLOG_PREAMBLE "discarding unsent tx sk_buff\n");
466 + atomic_inc(&ATM_SKB(*pskb)->vcc->stats->tx_err);
467 + myatmdd_free_tx_skb(*pskb);
469 + kfree(queue->start);
470 + kfree(queue->status);
471 + kfree(queue->pkt_len);
476 +/* returns non-zero for "out of space" */
477 +static int myatmdd_txq_enqueue(myatmdd_txq_t *queue, struct sk_buff *skb)
479 + /* increment head and wrap */
480 + struct sk_buff **newhead = queue->head + 1;
481 + if (newhead == queue->end)
482 + newhead = queue->start;
484 + DEBUG("%s\n", __FUNCTION__);
486 + /* abort if tx ring full */
487 + if (newhead == queue->tail)
490 + /* all is okay if we're here */
491 + *queue->head = skb;
492 + /* Tell hardware there's a buffer to send */
493 + queue->status[queue->head - queue->start] = TX_FULL;
494 + queue->pkt_len[queue->head - queue->start] = skb->len;
495 + queue->head = newhead;
499 +/* returns non-zero for "out of space" */
500 +static int myatmdd_rxq_enqueue(myatmdd_rxq_t *queue, struct sk_buff *skb /* empty buffer */)
502 + /* increment head and wrap */
503 + struct sk_buff **newhead = queue->head + 1;
504 + if (newhead == queue->end)
505 + newhead = queue->start;
507 + DEBUG("%s\n", __FUNCTION__);
509 + /* abort if rx ring full */
510 + if (newhead == queue->tail)
513 + /* all is okay if we're here */
514 + *queue->head = skb;
515 + /* Tell hardware there's a buffer to send */
516 + queue->status[queue->head - queue->start] = RX_FULL;
517 + queue->head = newhead;
521 +static struct sk_buff *myatmdd_txq_dequeue(myatmdd_txq_t *queue)
523 + DEBUG("%s\n", __FUNCTION__);
524 + if (queue->tail != queue->head && queue->status[queue->tail - queue->start] == TX_SENT)
526 + struct sk_buff *skb = *queue->tail;
528 + /* increment tail and wrap */
529 + struct sk_buff **newtail = queue->tail + 1;
530 + if (newtail == queue->end)
531 + newtail = queue->start;
532 + *queue->tail = NULL;
533 + queue->status[queue->tail - queue->start] = TX_EMPTY;
534 + queue->tail = newtail;
540 +/* returns NULL for "no new recvd frames" */
541 +static struct sk_buff *myatmdd_rxq_dequeue(myatmdd_rxq_t *queue, int *pkt_len)
543 + DEBUG("%s\n", __FUNCTION__);
544 + if (queue->tail != queue->head && queue->status[queue->tail - queue->start] == RX_RECVD)
546 + struct sk_buff *skb = *queue->tail;
548 + /* increment tail and wrap */
549 + struct sk_buff **newtail = queue->tail + 1;
550 + if (newtail == queue->end)
551 + newtail = queue->start;
552 + *queue->tail = NULL;
553 + queue->status[queue->tail - queue->start] = RX_EMPTY;
554 + *pkt_len = queue->pkt_len[queue->tail - queue->start];
555 + queue->tail = newtail;
562 +#########################################################
564 +# Functions : Implementations of function ptrs in
565 +# myatmdd_phy_ops. This is the phy driver
566 +# start: myatmdd_phy_start,
567 +# ioctl: myatmdd_phy_ioctl,
568 +# interrupt: myatmdd_phy_int,
570 +# Purpose : See ATM device driver interface v0.1
572 +# Notes : Conforming to Linux ATM device driver i/f
573 +# interface. Draft version 0.1
575 +# Designed to work with multiple devices
576 +##########################################################
579 +# AJZ 10Apr03 Created
580 +##########################################################
582 +static int myatmdd_phy_start(struct atm_dev *dev)
584 + /* Provide ATM driver with a pointer via which it
585 + * may invoke PHY driver's IOCTL or interrupt
588 + dev->phy = &myatmdd_phy_ops;
590 + /* If required allocate phy private data and save
591 + * pointer in dev->phy_data;
594 + /* TODO Initialise PHY hardware... */
599 +/* Should be called by SAR driver when it needs to handle an interrupt
600 + * triggered by PHY.
602 +static void myatmdd_phy_int(struct atm_dev *dev)
604 + /* Handle interrupt triggered by PHY */
607 +/* Gets called by SAR driver IOCTL handler for IOCTLS it doesn't recognise */
608 +static int myatmdd_phy_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg)
612 +// case SONET_GETSTATZ:
619 +#########################################################
621 +# Function : myatmdd_free_tx_skb
623 +# Purpose : frees an sk_buff.
625 +# Args : skb=pointer to socket buffer
627 +# Notes : Tries to use the upper layer pop() function
628 +# but uses dev_kfree_skb() if this doesn't exist
629 +##########################################################
632 +# AJZ 10Apr03 Created
633 +##########################################################
635 +static void myatmdd_free_tx_skb(struct sk_buff *skb)
637 + struct atm_vcc *vcc;
639 + DEBUG("%s\n", __FUNCTION__);
641 + /* See if we can use the VCC pop function */
642 + if (((vcc = ATM_SKB(skb)->vcc) != NULL) && (vcc->pop != NULL))
644 + /* Yes, so use ATM socket layer pop function */
645 + vcc->pop(vcc, skb);
649 + printk(KERN_WARNING KLOG_PREAMBLE "unable to call skb free function\n");
650 + /* No, so free socket buffer */
651 + dev_kfree_skb(skb);
656 +#########################################################
658 +# Functions : Implementations of function ptrs in
666 +# myatmdd_sg_send(),
667 +# myatmdd_change_qos(),
668 +# myatmdd_proc_read()
671 +# Purpose : See ATM device driver interface v0.1
673 +# Notes : Conforming to Linux ATM device driver i/f
674 +# interface. Draft version 0.1
676 +# Designed to work with multiple devices
677 +##########################################################
680 +# AJZ 10Apr03 Created
681 +##########################################################
683 +static int myatmdd_open(struct atm_vcc *vcc)
685 + myatmdd_vccdata_t *priv;
688 + DEBUG("%s\n", __FUNCTION__);
690 + /* Make sure we are opening a AAL0 or AAL5 connection */
691 + if ((vcc->qos.aal != ATM_AAL5) && (vcc->qos.aal != ATM_AAL0))
693 + printk(KERN_WARNING KLOG_PREAMBLE "invalid AAL\n");
697 + /* Address is in use */
698 + set_bit(ATM_VF_ADDR, &vcc->flags);
700 + /* Allocate some vcc-private memory */
701 + vcc->dev_data = kmalloc(sizeof(myatmdd_vccdata_t), GFP_KERNEL);
702 + if (vcc->dev_data == NULL)
704 + priv = vcc->dev_data;
706 + /* Setup the hardware for new VC... */
708 + /* Do not allow half open VCs - otherwise the example driver will not be able
709 + * to loop back frames sent !
711 + if (vcc->qos.rxtp.traffic_class == ATM_NONE || vcc->qos.txtp.traffic_class == ATM_NONE)
713 + kfree(vcc->dev_data);
717 + /* Create rx/tx queues for this VC */
718 + myatmdd_init_txq(&priv->txqueue, TXQ_SZ);
719 + myatmdd_init_rxq(&priv->rxqueue, RXQ_SZ);
721 + /* Fill rx queue with empty skbuffs */
722 + for (i = 0 ; i < RXQ_SZ - 1; i++)
724 + struct sk_buff *skb = dev_alloc_skb(AAL5_BUFLEN);
725 + myatmdd_rxq_enqueue(&priv->rxqueue,skb);
728 + /* Connection is now ready to receive data */
729 + set_bit(ATM_VF_READY, &vcc->flags);
734 +static void myatmdd_close(struct atm_vcc *vcc)
736 + myatmdd_vccdata_t *priv = vcc->dev_data;
738 + DEBUG("%s\n", __FUNCTION__);
740 + /* Indicate channel closed */
741 + clear_bit(ATM_VF_READY, &vcc->flags);
743 + /* TODO Uninitialise the hardware for this VC... */
745 + /* empty the rx and tx queues */
746 + myatmdd_release_txq(&priv->txqueue);
747 + myatmdd_release_rxq(&priv->rxqueue);
749 + /* Free the vcc-private memory */
750 + kfree(vcc->dev_data);
753 +static int myatmdd_ioctl(struct atm_dev *dev, unsigned int cmd,void *arg)
755 + /* myatmdd does not currently have an ioctl interface so pass ioctl onto PHY */
756 + if (dev->phy && dev->phy->ioctl) {
757 + return dev->phy->ioctl(dev, cmd, arg);
772 +/* Note may be called in either process or interrupt context! */
773 +static int myatmdd_send(struct atm_vcc *vcc,struct sk_buff *skb)
775 + myatmdd_vccdata_t *priv = vcc->dev_data;
777 + DEBUG("%s\n", __FUNCTION__);
779 + /* Assign VCC to socket buffer
780 + * Note: this must be done before attempting to call
781 + * myatmdd_free_tx_skb() as this may use ATM_SKB(skb)->vcc->pop()
783 + ATM_SKB(skb)->vcc = vcc;
785 + /* Setup hardware to send and arrange callback of myatmdd_send_complete... */
787 + /* In this example ATM device driver all VCs are looped back.
788 + * So copy to the rxq and emulate an rx interrupt
791 + /* Can we accept another skb to send ? */
792 + if (myatmdd_txq_enqueue(&priv->txqueue, skb))
794 + /* No - free socket buffer */
795 + myatmdd_free_tx_skb(skb);
797 + /* Update tx channel stats */
798 + atomic_inc(&vcc->stats->tx_err);
800 + /* Tell protocol layer to back off */
804 + /* This is the bit which copies the tx ring to the rx ring,
805 + * and triggers emulated rx and tx interrupts
807 + myatmdd_emulate_loopback_hardware(vcc);
812 +static int myatmdd_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs)
817 +static int myatmdd_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
819 + int left = (int) *pos;
822 + return sprintf(page, "1st line of stats\n");
824 + return sprintf(page, "2nd line of stats\n");
826 + return sprintf(page, "3rd line of stats\n");
832 +#########################################################
834 +# Function : myatmdd_init
836 +# Purpose : init the module, init and register the ATM device
840 +# Returns : return code
844 +##########################################################
847 +# AJZ 10Apr03 Created
848 +##########################################################
850 +int __init myatmdd_init(void)
852 + myatmdd_devdata_t *priv = kmalloc(sizeof(myatmdd_devdata_t),GFP_KERNEL);
857 + /* Register the new device */
858 + myatmdd_dev = atm_dev_register(MYATMDD,NULL,&myatmdd_ops,-1,NULL);
860 + /* Were we able to register this device? */
861 + if (myatmdd_dev == NULL)
863 + printk(KERN_ERR KLOG_PREAMBLE "failed to register CPM ATM device\n");
867 + /* Save pointer to device private data */
868 + myatmdd_dev->dev_data = priv;
870 + /* Initialise device parameters */
871 + myatmdd_dev->ci_range.vpi_bits = MYATMDD_VPI_BITS;
872 + myatmdd_dev->ci_range.vci_bits = MYATMDD_VCI_BITS;
873 + myatmdd_dev->link_rate = MYATMDD_PCR;
875 + /* Set up phy device */
876 + myatmdd_phy_start(myatmdd_dev);
878 + /* TODO Initialise SAR hardware... */
880 + /* Console output */
881 + printk(KERN_INFO KLOG_PREAMBLE "Initialised\n");
887 + * module_init() is called by insmod, if built as module,
888 + * or by do_initcalls(), if built as a resident driver.
890 +module_init(myatmdd_init);
893 +#########################################################
895 +# Function : myatmdd_exit
897 +# Purpose : delete module, uninit and dereg ATM device
905 +##########################################################
908 +# AJZ 10Apr03 Created
909 +##########################################################
913 +static void __exit myatmdd_exit(void)
915 + /* Disable SAR hardware... */
917 + /* Console output */
918 + printk(KERN_ERR KLOG_PREAMBLE "Uninitialised\n");
919 + kfree(myatmdd_dev->dev_data);
920 + atm_dev_deregister(myatmdd_dev);
922 +module_exit(myatmdd_exit);
925 diff -urN linux-5.9/drivers.org/atm/Kconfig linux-5.9/drivers/atm/Kconfig
926 --- linux-5.9/drivers.org/atm/Kconfig 2020-10-11 23:15:50.000000000 +0200
927 +++ linux-5.9/drivers/atm/Kconfig 2020-11-03 13:31:48.120071319 +0100
930 if ATM_DRIVERS && NETDEVICES && ATM
933 + tristate "ATM loopback"
934 + depends on INET && ATM
936 + This is an example atm driver. It does not require any actual ATM
937 + hardware. It supports AAL5 and AAL0. Frames are merely looped back
938 + to the sender on the same VC they were sent.
941 tristate "Dummy ATM driver"
943 diff -urN linux-5.9/drivers.org/atm/Makefile linux-5.9/drivers/atm/Makefile
944 --- linux-5.9/drivers.org/atm/Makefile 2020-10-11 23:15:50.000000000 +0200
945 +++ linux-5.9/drivers/atm/Makefile 2020-11-03 13:31:48.120071319 +0100
949 obj-$(CONFIG_ATM_DUMMY) += adummy.o
950 +obj-$(CONFIG_ATM_DD) += atmdd.o
951 obj-$(CONFIG_ATM_TCP) += atmtcp.o
952 obj-$(CONFIG_ATM_LANAI) += lanai.o