📄 af_wanpipe.c
字号:
sk_free(sk); return NULL; } memset(wan_opt, 0x00, sizeof(struct wanpipe_opt)); sk->protinfo.af_wanpipe = wan_opt; sk->protinfo.destruct_hook = wan_opt; /* Use timer to send data to the driver. This will act * as a BH handler for sendmsg functions */ sk->protinfo.af_wanpipe->tx_timer.data=(unsigned long)sk; sk->protinfo.af_wanpipe->tx_timer.function=wanpipe_delayed_transmit; MOD_INC_USE_COUNT; sock_init_data(NULL, sk); return sk;}/*============================================================ * wanpipe_sendmsg * * This function implements a sendto() system call, * for AF_WANPIPE socket family. * During socket bind() sk->bound_dev_if is initialized * to a correct network device. This number is used * to find a network device to which the packet should * be passed to. * * Each packet is queued into sk->write_queue and * delayed transmit bottom half handler is marked for * execution. * * A socket must be in WANSOCK_CONNECTED state before * a packet is queued into sk->write_queue. *===========================================================*/static int wanpipe_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm){ struct sock *sk = sock->sk; struct wan_sockaddr_ll *saddr=(struct wan_sockaddr_ll *)msg->msg_name; struct sk_buff *skb; netdevice_t *dev; unsigned short proto; unsigned char *addr; int ifindex, err, reserve = 0; if (!sk->zapped) return -ENETDOWN; if (sk->state != WANSOCK_CONNECTED) return -ENOTCONN; if (msg->msg_flags&~MSG_DONTWAIT) return(-EINVAL); /* it was <=, now one can send * zero length packets */ if (len < sizeof(x25api_hdr_t)) return -EINVAL; if (saddr == NULL) { ifindex = sk->bound_dev_if; proto = sk->num; addr = NULL; }else{ if (msg->msg_namelen < sizeof(struct wan_sockaddr_ll)){ return -EINVAL; } ifindex = sk->bound_dev_if; proto = saddr->sll_protocol; addr = saddr->sll_addr; } dev = dev_get_by_index(ifindex); if (dev == NULL){ printk(KERN_INFO "wansock: Send failed, dev index: %i\n",ifindex); return -ENXIO; } dev_put(dev); if (sock->type == SOCK_RAW) reserve = dev->hard_header_len; if (len > dev->mtu+reserve){ return -EMSGSIZE; } #ifndef LINUX_2_4 dev_lock_list(); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3) skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, msg->msg_flags & MSG_DONTWAIT, &err); #else skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, msg->msg_flags & MSG_DONTWAIT, &err); #endif if (skb==NULL){ goto out_unlock; } skb_reserve(skb, (dev->hard_header_len+15)&~15); skb->nh.raw = skb->data; /* Returns -EFAULT on error */ err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); if (err){ goto out_free; } if (dev->hard_header) { int res; err = -EINVAL; res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len); if (res<0){ goto out_free; } } skb->protocol = proto; skb->dev = dev; skb->priority = sk->priority; skb->pkt_type = WAN_PACKET_DATA; err = -ENETDOWN; if (!(dev->flags & IFF_UP)) goto out_free; #ifndef LINUX_2_4 dev_unlock_list(); #endif if (atomic_read(&sk->wmem_alloc) + skb->truesize > (unsigned int)sk->sndbuf){ kfree_skb(skb); return -ENOBUFS; } skb_queue_tail(&sk->write_queue,skb); atomic_inc(&sk->protinfo.af_wanpipe->packet_sent); if (!(test_and_set_bit(0,&sk->protinfo.af_wanpipe->timer))){ del_timer(&sk->protinfo.af_wanpipe->tx_timer); sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+1; add_timer(&sk->protinfo.af_wanpipe->tx_timer); } return(len);out_free: kfree_skb(skb);out_unlock:#ifndef LINUX_2_4 dev_unlock_list();#endif return err;}/*============================================================ * wanpipe_delayed_tarnsmit * * Transmit bottom half handeler. It dequeues packets * from sk->write_queue and passes them to the * driver. If the driver is busy, the packet is * re-enqueued. * * Packet Sent counter is decremented on successful * transmission. *===========================================================*/static void wanpipe_delayed_transmit (unsigned long data){ struct sock *sk=(struct sock *)data; struct sk_buff *skb; netdevice_t *dev = sk->protinfo.af_wanpipe->dev; sdla_t *card = (sdla_t*)sk->protinfo.af_wanpipe->card; if (!card || !dev){ clear_bit (0,&sk->protinfo.af_wanpipe->timer); DBG_PRINTK(KERN_INFO "wansock: Transmit delay, no dev or card\n"); return; } if (sk->state != WANSOCK_CONNECTED || !sk->zapped){ clear_bit (0,&sk->protinfo.af_wanpipe->timer); DBG_PRINTK(KERN_INFO "wansock: Tx Timer, State not CONNECTED\n"); return; } /* If driver is executing command, we must offload * the board by not sending data. Otherwise a * pending command will never get a free buffer * to execute */ if (atomic_read(&card->u.x.command_busy)){ sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+SLOW_BACKOFF; add_timer(&sk->protinfo.af_wanpipe->tx_timer); DBG_PRINTK(KERN_INFO "wansock: Tx Timer, command bys BACKOFF\n"); return; } if (test_and_set_bit(0,&wanpipe_tx_critical)){ printk(KERN_INFO "WanSock: Tx timer critical %s\n",dev->name); sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+SLOW_BACKOFF; add_timer(&sk->protinfo.af_wanpipe->tx_timer); return; } /* Check for a packet in the fifo and send */ if ((skb=skb_dequeue(&sk->write_queue)) != NULL){ if (dev->hard_start_xmit(skb, dev) != 0){ /* Driver failed to transmit, re-enqueue * the packet and retry again later */ skb_queue_head(&sk->write_queue,skb); clear_bit(0,&wanpipe_tx_critical); return; }else{ /* Packet Sent successful. Check for more packets * if more packets, re-trigger the transmit routine * other wise exit */ atomic_dec(&sk->protinfo.af_wanpipe->packet_sent); if (skb_peek(&sk->write_queue) == NULL){ /* If there is nothing to send, kick * the poll routine, which will trigger * the application to send more data */ sk->data_ready(sk,0); clear_bit (0,&sk->protinfo.af_wanpipe->timer); }else{ /* Reschedule as fast as possible */ sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+1; add_timer(&sk->protinfo.af_wanpipe->tx_timer); } } } clear_bit(0,&wanpipe_tx_critical);}/*============================================================ * execute_command * * Execute x25api commands. The atomic variable * chan->command is used to indicate to the driver that * command is pending for exection. The acutal command * structure is placed into a sock mbox structure * (sk->protinfo.af_wanpipe->mbox). * * The sock private structure, mbox is * used as shared memory between sock and the driver. * Driver uses the sock mbox to execute the command * and return the result. * * For all command except PLACE CALL, the function * waits for the result. PLACE CALL can be ether * blocking or nonblocking. The user sets this option * via ioctl call. *===========================================================*/static int execute_command(struct sock *sk, unsigned char cmd, unsigned int flags){ netdevice_t *dev; wanpipe_common_t *chan=NULL; int err=0; DECLARE_WAITQUEUE(wait, current); dev = dev_get_by_index(sk->bound_dev_if); if (dev == NULL){ printk(KERN_INFO "wansock: Exec failed no dev %i\n", sk->bound_dev_if); return -ENODEV; } dev_put(dev); if ((chan=dev->priv) == NULL){ printk(KERN_INFO "wansock: Exec cmd failed no priv area\n"); return -ENODEV; } if (atomic_read(&chan->command)){ printk(KERN_INFO "wansock: ERROR: Command already running %x, %s\n", atomic_read(&chan->command),dev->name); return -EINVAL; } if (!sk->protinfo.af_wanpipe->mbox){ printk(KERN_INFO "wansock: In execute without MBOX\n"); return -EINVAL; } ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.command=cmd; ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn = sk->protinfo.af_wanpipe->lcn; ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.result=0x7F; if (flags & O_NONBLOCK){ cmd |= 0x80; atomic_set(&chan->command, cmd); }else{ atomic_set(&chan->command, cmd); } add_wait_queue(sk->sleep,&wait); current->state = TASK_INTERRUPTIBLE; for (;;){ if (((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.result != 0x7F) { err = 0; break; } if (signal_pending(current)) { err = -ERESTARTSYS; break; } schedule(); } current->state = TASK_RUNNING; remove_wait_queue(sk->sleep,&wait); return err;}/*============================================================ * wanpipe_destroy_timer * * Used by wanpipe_release, to delay release of * the socket. *===========================================================*/static void wanpipe_destroy_timer(unsigned long data){ struct sock *sk=(struct sock *)data; if ((!atomic_read(&sk->wmem_alloc) && !atomic_read(&sk->rmem_alloc)) || (++sk->protinfo.af_wanpipe->force == 5)) { if (atomic_read(&sk->wmem_alloc) || atomic_read(&sk->rmem_alloc)) printk(KERN_INFO "wansock: Warning, Packet Discarded due to sock shutdown!\n"); if (sk->protinfo.af_wanpipe){ kfree(sk->protinfo.af_wanpipe); sk->protinfo.af_wanpipe=NULL; } #ifdef LINUX_2_4 if (atomic_read(&sk->refcnt) != 1){ atomic_set(&sk->refcnt,1); DBG_PRINTK(KERN_INFO "wansock: Error, wrong reference count: %i ! :delay.\n", atomic_read(&sk->refcnt)); } sock_put(sk); #else sk_free(sk); #endif atomic_dec(&wanpipe_socks_nr); MOD_DEC_USE_COUNT; return; } sk->timer.expires=jiffies+5*HZ; add_timer(&sk->timer); printk(KERN_INFO "wansock: packet sk destroy delayed\n");}/*============================================================ * wanpipe_unlink_driver * * When the socket is released, this function is * used to remove links that bind the sock and the * driver together. *===========================================================*/static void wanpipe_unlink_driver (struct sock *sk){ netdevice_t *dev; wanpipe_common_t *chan=NULL; sk->zapped=0; sk->state = WANSOCK_DISCONNECTED; sk->protinfo.af_wanpipe->dev = NULL; dev = dev_get_by_index(sk->bound_dev_if); if (!dev){ printk(KERN_INFO "wansock: No dev on release\n"); return; } dev_put(dev); if ((chan = dev->priv) == NULL){ printk(KERN_INFO "wansock: No Priv Area on release\n"); return; } set_bit(0,&chan->common_critical); chan->sk=NULL; chan->func=NULL; chan->mbox=NULL; chan->tx_timer=NULL; clear_bit(0,&chan->common_critical); release_device(dev); return;}/*============================================================ * wanpipe_link_driver * * Upon successful bind(), sock is linked to a driver * by binding in the wanpipe_rcv() bottom half handler * to the driver function pointer, as well as sock and * sock mailbox addresses. This way driver can pass * data up the socket. *===========================================================*/static void wanpipe_link_driver (netdevice_t *dev, struct sock *sk){ wanpipe_common_t *chan = dev->priv; if (!chan) return; set_bit(0,&chan->common_critical); chan->sk=sk; chan->func=wanpipe_rcv; chan->mbox=sk->protinfo.af_wanpipe->mbox; chan->tx_timer = &sk->protinfo.af_wanpipe->tx_timer; sk->protinfo.af_wanpipe->dev=dev; sk->zapped = 1; clear_bit(0,&chan->common_critical);}/*============================================================ * release_device * * During sock release, clear a critical bit, which * marks the device a being taken. *===========================================================*/static void release_device (netdevice_t *dev){ wanpipe_common_t *chan=dev->priv; clear_bit(0,(void*)&chan->rw_bind);}/*============================================================ * wanpipe_release * * Close a PACKET socket. This is fairly simple. We * immediately go to 'closed' state and remove our * protocol entry in the device list. *===========================================================*/#ifdef LINUX_2_4static int wanpipe_release(struct socket *sock)#elsestatic int wanpipe_release(struct socket *sock, struct socket *peersock)#endif{ #ifndef LINUX_2_4 struct sk_buff *skb;#endif struct sock *sk = sock->sk; struct sock **skp; if (!sk) return 0; check_write_queue(sk); /* Kill the tx timer, if we don't kill it now, the timer * will run after we kill the sock. Timer code will * try to access the sock which has been killed and cause * kernel panic */ del_timer(&sk->protinfo.af_wanpipe->tx_timer); /* * Unhook packet receive handler. */ if (sk->num == htons(X25_PROT) && sk->state != WANSOCK_DISCONNECTED && sk->zapped){ netdevice_t *dev = dev_get_by_index(sk->bound_dev_if); wanpipe_common_t *chan; if (dev){ chan=dev->priv; atomic_set(&chan->disconnect,1); DBG_PRINTK(KERN_INFO "wansock: Sending Clear Indication %i\n", sk->state); dev_put(dev); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -