📄 auerisdn.c
字号:
spin_lock_irqsave(&ahp->seq_lock, flags); ahp->txseq = 0; ahp->rxseq = 0; spin_unlock_irqrestore(&ahp->seq_lock, flags); l2_header[l2_index++] = 0x73; /* UA */ goto phd_answer; case 0x53: /* DISC */ dbg("DISC"); /* Send back a UA */ l2_header[l2_index++] = 0x73; /* UA */ goto phd_answer; default: dbg("Unhandled L2 Message %X", (int) c); break; } /* all done */ goto phd_free; /* we have to generate a local answer */ /* first, confirm old message, free old skb */ phd_answer:auerisdn_d_confirmskb(cp, skb); /* allocate a new skbuff */ skb = dev_alloc_skb(l2_index); if (!skb) { err("no memory for new skb"); break; } dump("local answer to L2 is:", l2_header, l2_index); memcpy(skb_put(skb, l2_index), l2_header, l2_index); auerisdn_d_l1l2(&cp->isdn, PH_DATA | INDICATION, skb); break; /* we have to send the L3 message out */ phd_send:if (!len) goto phd_free; /* no message left */ /* get a new data buffer */ bp = auerbuf_getbuf(&cp->bufctl); if (!bp) { warn("no auerbuf free"); goto phd_free; } /* protect against too big write requests */ /* Should not happen */ if (len > cp->maxControlLength) { err("too long D-channel paket truncated"); len = cp->maxControlLength; } /* Copy the data */ memcpy(bp->bufp + AUH_SIZE, sp, len); /* set the header byte */ *(bp->bufp) = cp->isdn.dchannelservice. id | AUH_DIRECT | AUH_UNSPLIT; /* Set the transfer Parameters */ bp->len = len + AUH_SIZE; bp->dr->bRequestType = AUT_WREQ; bp->dr->bRequest = AUV_WBLOCK; bp->dr->wValue = cpu_to_le16(0); bp->dr->wIndex = cpu_to_le16(cp->isdn.dchannelservice. id | AUH_DIRECT | AUH_UNSPLIT); bp->dr->wLength = cpu_to_le16(len + AUH_SIZE); FILL_CONTROL_URB(bp->urbp, cp->usbdev, usb_sndctrlpipe(cp->usbdev, 0), (unsigned char *) bp->dr, bp->bufp, len + AUH_SIZE, auerisdn_dcw_complete, bp); /* up we go */ ret = auerchain_submit_urb(&cp->controlchain, bp->urbp); if (ret) auerisdn_dcw_complete(bp->urbp); else dbg("auerisdn_dwrite: Write OK"); /* confirm message, free skb */ phd_free:auerisdn_d_confirmskb(cp, skb); break; default: warn("pr %#x\n", pr); break; } } else { /* hisax interface is down */ switch (pr) { case PH_ACTIVATE | REQUEST: /* activation request */ dbg("D channel PH_ACTIVATE | REQUEST with interface down"); /* don't answer this request! Endless... */ break; case PH_DEACTIVATE | REQUEST: /* deactivation request */ dbg("D channel PH_DEACTIVATE | REQUEST with interface down"); hisax_d_if->l1l2(hisax_d_if, PH_DEACTIVATE | INDICATION, NULL); break; case PH_DATA | REQUEST: /* Transmit data request */ dbg("D channel PH_DATA | REQUEST with interface down"); skb = (struct sk_buff *) arg; /* free data buffer */ if (skb) { skb_pull(skb, skb->len); dev_kfree_skb_any(skb); } /* send confirmation back to layer 2 */ hisax_d_if->l1l2(hisax_d_if, PH_DATA | CONFIRM, NULL); break; default: warn("pr %#x\n", pr); break; } }}/* Completion function for D channel open */static void auerisdn_dcopen_complete(struct urb *urbp){ struct auerbuf *bp = (struct auerbuf *) urbp->context; struct auerswald *cp = ((struct auerswald *) ((char *) (bp->list) - (unsigned long) (&((struct auerswald *) 0)-> bufctl))); dbg("auerisdn_dcopen_complete called"); auerbuf_releasebuf(bp); /* Wake up all processes waiting for a buffer */ wake_up(&cp->bufferwait);}/* Open the D-channel once more */static void auerisdn_dcopen(unsigned long data){ struct auerswald *cp = (struct auerswald *) data; struct auerbuf *bp; int ret; if (cp->disconnecting) return; dbg("auerisdn_dcopen running"); /* get a buffer for the command */ bp = auerbuf_getbuf(&cp->bufctl); /* if no buffer available: can't change the mode */ if (!bp) { err("auerisdn_dcopen: no data buffer available"); return; } /* fill the control message */ bp->dr->bRequestType = AUT_WREQ; bp->dr->bRequest = AUV_CHANNELCTL; bp->dr->wValue = cpu_to_le16(1); bp->dr->wIndex = cpu_to_le16(0); bp->dr->wLength = cpu_to_le16(0); FILL_CONTROL_URB(bp->urbp, cp->usbdev, usb_sndctrlpipe(cp->usbdev, 0), (unsigned char *) bp->dr, bp->bufp, 0, (usb_complete_t) auerisdn_dcopen_complete, bp); /* submit the control msg */ ret = auerchain_submit_urb(&cp->controlchain, bp->urbp); dbg("dcopen submitted"); if (ret) { bp->urbp->status = ret; auerisdn_dcopen_complete(bp->urbp); } return;}/* Initialize the isdn related items in struct auerswald */void auerisdn_init_dev(struct auerswald *cp){ unsigned int u; cp->isdn.dchannelservice.id = AUH_UNASSIGNED; cp->isdn.dchannelservice.dispatch = auerisdn_dispatch_dc; cp->isdn.dchannelservice.disconnect = auerisdn_disconnect_dc; init_timer(&cp->isdn.dcopen_timer); cp->isdn.dcopen_timer.data = (unsigned long) cp; cp->isdn.dcopen_timer.function = auerisdn_dcopen; for (u = 0; u < AUISDN_BCHANNELS; u++) { cp->isdn.bc[u].cp = cp; cp->isdn.bc[u].mode = L1_MODE_NULL; cp->isdn.bc[u].channel = u; spin_lock_init(&cp->isdn.bc[u].txskb_lock); }}/* Connect to the HISAX interface. Returns 0 if successfull */int auerisdn_probe(struct auerswald *cp){ struct hisax_b_if *b_if[AUISDN_BCHANNELS]; struct usb_endpoint_descriptor *ep; struct auerhisax *ahp; DECLARE_WAIT_QUEUE_HEAD(wqh); unsigned int u; unsigned char *ucp; unsigned int first_time; int ret; /* First allocate resources, then register hisax interface */ /* Allocate RX buffers */ for (u = 0; u < AUISDN_BCHANNELS; u++) { if (!cp->isdn.bc[u].rxbuf) { cp->isdn.bc[u].rxbuf = (char *) kmalloc(AUISDN_RXSIZE, GFP_KERNEL); if (!cp->isdn.bc[u].rxbuf) { err("can't allocate buffer for B channel RX data"); return -1; } } } /* Read out B-Channel output fifo size */ ucp = kmalloc(32, GFP_KERNEL); if (!ucp) { err("Out of memory"); return -3; } ret = usb_control_msg(cp->usbdev, /* pointer to device */ usb_rcvctrlpipe(cp->usbdev, 0), /* pipe to control endpoint */ AUV_GETINFO, /* USB message request value */ AUT_RREQ, /* USB message request type value */ 0, /* USB message value */ AUDI_OUTFSIZE, /* USB message index value */ ucp, /* pointer to the receive buffer */ 32, /* length of the buffer */ HZ * 2); /* time to wait for the message to complete before timing out */ if (ret < 4) { kfree(ucp); err("can't read TX Fifo sizes for B1,B2"); return -4; } for (u = 0; u < AUISDN_BCHANNELS; u++) { ret = le16_to_cpup(ucp + u * 2); cp->isdn.bc[u].ofsize = ret; cp->isdn.bc[u].txfree = ret; } kfree(ucp); for (u = 0; u < AUISDN_BCHANNELS; u++) { dbg("B%d buffer size is %d", u, cp->isdn.bc[u].ofsize); } /* get the B channel output INT size */ cp->isdn.intbo_endp = AU_IRQENDPBO; ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_OUT | AU_IRQENDPBO); if (!ep) { /* Some devices have another endpoint number here ... */ cp->isdn.intbo_endp = AU_IRQENDPBO_2; ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_OUT | AU_IRQENDPBO_2); if (!ep) { err("can't get B channel OUT endpoint"); return -5; } } cp->isdn.outsize = ep->wMaxPacketSize; cp->isdn.outInterval = ep->bInterval; cp->isdn.usbdev = cp->usbdev; /* allocate the urb and data buffer */ if (!cp->isdn.intbo_urbp) { cp->isdn.intbo_urbp = usb_alloc_urb(0); if (!cp->isdn.intbo_urbp) { err("can't allocate urb for B channel output endpoint"); return -6; } } if (!cp->isdn.intbo_bufp) { cp->isdn.intbo_bufp = (char *) kmalloc(cp->isdn.outsize, GFP_KERNEL); if (!cp->isdn.intbo_bufp) { err("can't allocate buffer for B channel output endpoint"); return -7; } } /* get the B channel input INT size */ ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_IN | AU_IRQENDPBI); if (!ep) { err("can't get B channel IN endpoint"); return -8; } cp->isdn.insize = ep->wMaxPacketSize; /* allocate the urb and data buffer */ if (!cp->isdn.intbi_urbp) { cp->isdn.intbi_urbp = usb_alloc_urb(0); if (!cp->isdn.intbi_urbp) { err("can't allocate urb for B channel input endpoint"); return -9; } } if (!cp->isdn.intbi_bufp) { cp->isdn.intbi_bufp = (char *) kmalloc(cp->isdn.insize, GFP_KERNEL); if (!cp->isdn.intbi_bufp) { err("can't allocate buffer for B channel input endpoint"); return -10; } } /* setup urb */ FILL_INT_URB(cp->isdn.intbi_urbp, cp->usbdev, usb_rcvintpipe(cp->usbdev, AU_IRQENDPBI), cp->isdn.intbi_bufp, cp->isdn.insize, auerisdn_intbi_complete, cp, ep->bInterval); /* start the urb */ cp->isdn.intbi_urbp->status = 0; /* needed! */ ret = usb_submit_urb(cp->isdn.intbi_urbp); if (ret < 0) { err("activation of B channel input int failed %d", ret); usb_free_urb(cp->isdn.intbi_urbp); cp->isdn.intbi_urbp = NULL; return -11; } /* request the D-channel service now */ dbg("Requesting D channel now"); cp->isdn.dchannelservice.id = AUH_DCHANNEL; if (auerswald_addservice(cp, &cp->isdn.dchannelservice)) { err("can not open D-channel"); cp->isdn.dchannelservice.id = AUH_UNASSIGNED; return -2; } /* Find a free hisax interface */ for (u = 0; u < AUER_MAX_DEVICES; u++) { ahp = &auerhisax_table[u]; if (!ahp->cp) { first_time = (u == 0); goto ahp_found; } } /* no free interface found */ return -12; /* we found a free hisax interface */ ahp_found: /* Wait until ipppd timeout expired. The reason behind this ugly construct: If we connect to a hisax device without waiting for ipppd we are not able to make a new IP connection. */ if (ahp->last_close) { unsigned long timeout = jiffies - ahp->last_close; if (timeout < AUISDN_IPTIMEOUT) { info("waiting for ipppd to timeout"); sleep_on_timeout(&wqh, AUISDN_IPTIMEOUT - timeout); } } cp->isdn.ahp = ahp; u = ahp->hisax_registered; ahp->hisax_registered = 1; ahp->cp = cp; /* now do the registration */ if (!u) { for (u = 0; u < AUISDN_BCHANNELS; u++) { b_if[u] = &ahp->hisax_b_if[u]; } if (hisax_register (&ahp->hisax_d_if, b_if, "auerswald_usb", ISDN_PTYPE_EURO)) { err("hisax registration failed"); ahp->cp = NULL; cp->isdn.ahp = NULL; ahp->hisax_registered = 0; return -13; } dbg("hisax interface registered"); } /* send a D channel L1 activation indication to hisax */ auerisdn_d_l1l2(&cp->isdn, PH_ACTIVATE | INDICATION, NULL); cp->isdn.dc_activated = 1; /* do another D channel activation for problematic devices */ cp->isdn.dcopen_timer.expires = jiffies + HZ; dbg("add timer"); add_timer(&cp->isdn.dcopen_timer); return 0;}/* The USB device was disconnected */void auerisdn_disconnect(struct auerswald *cp){ struct auerhisax *ahp; DECLARE_WAIT_QUEUE_HEAD(wqh); unsigned long flags; unsigned int u; int ret; unsigned int stop_bc; dbg("auerisdn_disconnect called"); /* stop a running timer */ del_timer_sync(&cp->isdn.dcopen_timer); /* first, stop the B channels */ stop_bc = auerisdn_b_disconnect(cp); /* stop the D channels */ auerisdn_d_l1l2(&cp->isdn, PH_DEACTIVATE | INDICATION, NULL); cp->isdn.dc_activated = 0; dbg("D-Channel disconnected"); /* Wait a moment */ sleep_on_timeout(&wqh, HZ / 10); /* Shut the connection to the hisax interface */ ahp = cp->isdn.ahp; if (ahp) { dbg("closing connection to hisax interface"); ahp->cp = NULL; cp->isdn.ahp = NULL; /* time of last closure */ if (stop_bc) /* if we kill a running connection ... */ ahp->last_close = jiffies; else ahp->last_close = 0; } /* Now free the memory */ if (cp->isdn.intbi_urbp) { ret = usb_unlink_urb(cp->isdn.intbi_urbp); if (ret) dbg("B in: nonzero int unlink result received: %d", ret); usb_free_urb(cp->isdn.intbi_urbp); cp->isdn.intbi_urbp = NULL; } kfree(cp->isdn.intbi_bufp); cp->isdn.intbi_bufp = NULL; if (cp->isdn.intbo_urbp) { cp->isdn.intbo_urbp->transfer_flags &= ~USB_ASYNC_UNLINK; ret = usb_unlink_urb(cp->isdn.intbo_urbp); if (ret) dbg("B out: nonzero int unlink result received: %d", ret); usb_free_urb(cp->isdn.intbo_urbp); cp->isdn.intbo_urbp = NULL; } kfree(cp->isdn.intbo_bufp); cp->isdn.intbo_bufp = NULL; /* Remove the rx and tx buffers */ for (u = 0; u < AUISDN_BCHANNELS; u++) { kfree(cp->isdn.bc[u].rxbuf); cp->isdn.bc[u].rxbuf = NULL; spin_lock_irqsave(&cp->isdn.bc[u].txskb_lock, flags); if (cp->isdn.bc[u].txskb) { skb_pull(cp->isdn.bc[u].txskb, cp->isdn.bc[u].txskb->len); dev_kfree_skb_any(cp->isdn.bc[u].txskb); cp->isdn.bc[u].txskb = NULL; } spin_unlock_irqrestore(&cp->isdn.bc[u].txskb_lock, flags); } /* Remove the D-channel connection */ auerswald_removeservice(cp, &cp->isdn.dchannelservice);}/*-------------------------------------------------------------------*//* Environment for long-lasting hisax interface *//* Wrapper for hisax B0 channel L2L1 */static void auerisdn_b0_l2l1_wrapper(struct hisax_if *ifc, int pr, void *arg){ auerisdn_b_l2l1(ifc, pr, arg, 0);}/* Wrapper for hisax B1 channel L2L1 */static void auerisdn_b1_l2l1_wrapper(struct hisax_if *ifc, int pr, void *arg){ auerisdn_b_l2l1(ifc, pr, arg, 1);}/* Init the global variables */void auerisdn_init(void){ struct auerhisax *ahp; unsigned int u; memset(&auerhisax_table, 0, sizeof(auerhisax_table)); for (u = 0; u < AUER_MAX_DEVICES; u++) { ahp = &auerhisax_table[u]; spin_lock_init(&ahp->seq_lock); ahp->hisax_d_if.ifc.priv = ahp; ahp->hisax_d_if.ifc.l2l1 = auerisdn_d_l2l1; ahp->hisax_b_if[0].ifc.priv = ahp; ahp->hisax_b_if[0].ifc.l2l1 = auerisdn_b0_l2l1_wrapper; ahp->hisax_b_if[1].ifc.priv = ahp; ahp->hisax_b_if[1].ifc.l2l1 = auerisdn_b1_l2l1_wrapper; }}/* Deinit the global variables */void auerisdn_cleanup(void){ struct auerhisax *ahp; int i; /* cleanup last allocated device first */ for (i = AUER_MAX_DEVICES - 1; i >= 0; i--) { ahp = &auerhisax_table[i]; if (ahp->cp) { err("hisax device %d open at cleanup", i); } if (ahp->hisax_registered) { hisax_unregister(&ahp->hisax_d_if); dbg("hisax interface %d freed", i); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -