// SPDX-License-Identifier: GPL-2.0-or-later /* hfcsusb.c * mISDN driver for Colognechip HFC-S USB chip * * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de) * Copyright 2008 by Martin Bachem (info@bachem-it.com) * * module params * debug=<n>, default=0, with n=0xHHHHGGGG * H - l1 driver flags described in hfcsusb.h * G - common mISDN debug flags described at mISDNhw.h * * poll=<n>, default 128 * n : burst size of PH_DATA_IND at transparent rx data * * Revision: 0.3.3 (socket), 2008-11-05
*/
MODULE_AUTHOR("Martin Bachem");
MODULE_DESCRIPTION("mISDN driver for Colognechip HFC-S USB chip");
MODULE_LICENSE("GPL");
module_param(debug, uint, S_IRUGO | S_IWUSR);
module_param(poll, int, 0);
staticint hfcsusb_cnt;
/* some function prototypes */ staticvoid hfcsusb_ph_command(struct hfcsusb *hw, u_char command); staticvoid release_hw(struct hfcsusb *hw); staticvoid reset_hfcsusb(struct hfcsusb *hw); staticvoid setPortMode(struct hfcsusb *hw); staticvoid hfcsusb_start_endpoint(struct hfcsusb *hw, int channel); staticvoid hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel); staticint hfcsusb_setup_bch(struct bchannel *bch, int protocol); staticvoid deactivate_bchannel(struct bchannel *bch); staticint hfcsusb_ph_info(struct hfcsusb *hw);
/* start next background transfer for control channel */ staticvoid
ctrl_start_transfer(struct hfcsusb *hw)
{ if (debug & DBG_HFC_CALL_TRACE)
printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
bch = &hw->bch[rq->adr.channel - 1]; if (test_and_set_bit(FLG_OPEN, &bch->Flags)) return -EBUSY; /* b-channel can be only open once */
bch->ch.protocol = rq->protocol;
rq->ch = &bch->ch;
if (!try_module_get(THIS_MODULE))
printk(KERN_WARNING "%s: %s:cannot get module\n",
hw->name, __func__); return 0;
}
staticint
channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq)
{ int ret = 0;
/* collect data from incoming interrupt or isochron USB data */ staticvoid
hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsignedint len, int finish)
{ struct hfcsusb *hw = fifo->hw; struct sk_buff *rx_skb = NULL; int maxlen = 0; int fifon = fifo->fifonum; int i; int hdlc = 0; unsignedlong flags;
spin_lock_irqsave(&hw->lock, flags); if (fifo->dch) {
rx_skb = fifo->dch->rx_skb;
maxlen = fifo->dch->maxlen;
hdlc = 1;
} if (fifo->bch) { if (test_bit(FLG_RX_OFF, &fifo->bch->Flags)) {
fifo->bch->dropcnt += len;
spin_unlock_irqrestore(&hw->lock, flags); return;
}
maxlen = bchannel_get_rxbuf(fifo->bch, len);
rx_skb = fifo->bch->rx_skb; if (maxlen < 0) { if (rx_skb)
skb_trim(rx_skb, 0);
pr_warn("%s.B%d: No bufferspace for %d bytes\n",
hw->name, fifo->bch->nr, len);
spin_unlock_irqrestore(&hw->lock, flags); return;
}
maxlen = fifo->bch->maxlen;
hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags);
} if (fifo->ech) {
rx_skb = fifo->ech->rx_skb;
maxlen = fifo->ech->maxlen;
hdlc = 1;
}
if (fifo->dch || fifo->ech) { if (!rx_skb) {
rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); if (rx_skb) { if (fifo->dch)
fifo->dch->rx_skb = rx_skb; if (fifo->ech)
fifo->ech->rx_skb = rx_skb;
skb_trim(rx_skb, 0);
} else {
printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n",
hw->name, __func__);
spin_unlock_irqrestore(&hw->lock, flags); return;
}
} /* D/E-Channel SKB range check */ if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) {
printk(KERN_DEBUG "%s: %s: sbk mem exceeded " "for fifo(%d) HFCUSB_D_RX\n",
hw->name, __func__, fifon);
skb_trim(rx_skb, 0);
spin_unlock_irqrestore(&hw->lock, flags); return;
}
}
skb_put_data(rx_skb, data, len);
if (hdlc) { /* we have a complete hdlc packet */ if (finish) { if ((rx_skb->len > 3) &&
(!(rx_skb->data[rx_skb->len - 1]))) { if (debug & DBG_HFC_FIFO_VERBOSE) {
printk(KERN_DEBUG "%s: %s: fifon(%i)" " new RX len(%i): ",
hw->name, __func__, fifon,
rx_skb->len);
i = 0; while (i < rx_skb->len)
printk("%02x ",
rx_skb->data[i++]);
printk("\n");
}
/* remove CRC & status */
skb_trim(rx_skb, rx_skb->len - 3);
if (fifo->dch)
recv_Dchannel(fifo->dch); if (fifo->bch)
recv_Bchannel(fifo->bch, MISDN_ID_ANY,
0); if (fifo->ech)
recv_Echannel(fifo->ech,
&hw->dch);
} else { if (debug & DBG_HFC_FIFO_VERBOSE) {
printk(KERN_DEBUG "%s: CRC or minlen ERROR fifon(%i) " "RX len(%i): ",
hw->name, fifon, rx_skb->len);
i = 0; while (i < rx_skb->len)
printk("%02x ",
rx_skb->data[i++]);
printk("\n");
}
skb_trim(rx_skb, 0);
}
}
} else { /* deliver transparent data to layer2 */
recv_Bchannel(fifo->bch, MISDN_ID_ANY, false);
}
spin_unlock_irqrestore(&hw->lock, flags);
}
staticvoid
fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsignedint pipe, void *buf, int num_packets, int packet_size, int interval,
usb_complete_t complete, void *context)
{ int k;
/* * ISO transfer only partially completed, * look at individual frame status for details
*/ if (status == -EXDEV) { if (debug & DEBUG_HW)
printk(KERN_DEBUG "%s: %s: with -EXDEV " "urb->status %d, fifonum %d\n",
hw->name, __func__, status, fifon);
/* clear status, so go on with ISO transfers */
status = 0;
}
/* USB data log for every D ISO in */ if ((fifon == HFCUSB_D_RX) &&
(debug & DBG_HFC_USB_VERBOSE)) {
printk(KERN_DEBUG "%s: %s: %d (%d/%d) len(%d) ",
hw->name, __func__, urb->start_frame,
k, num_isoc_packets - 1,
len); for (i = 0; i < len; i++)
printk("%x ", buf[i]);
printk("\n");
}
if (!iso_status) { if (fifo->last_urblen != maxlen) { /* * save fifo fill-level threshold bits * to use them later in TX ISO URB * completions
*/
hw->threshold_mask = buf[1];
if (fifon == HFCUSB_D_RX)
s0_state = (buf[0] >> 4);
/* USB data log for every D INT in */ if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) {
printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ",
hw->name, __func__, len); for (i = 0; i < len; i++)
printk("%02x ", buf[i]);
printk("\n");
}
if (fifo->last_urblen != fifo->usb_packet_maxlen) { /* the threshold mask is in the 2nd status byte */
hw->threshold_mask = buf[1];
/* signal S0 layer1 state change */ if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) {
hw->dch.state = (buf[0] >> 4);
schedule_event(&hw->dch, FLG_PHCHANGE);
}
eof[fifon] = buf[0] & 1; /* if we have more than the 2 status bytes -> collect data */ if (len > 2)
hfcsusb_rx_frame(fifo, buf + 2,
urb->actual_length - 2,
(len < maxlen) ? eof[fifon] : 0);
} else {
hfcsusb_rx_frame(fifo, buf, urb->actual_length,
(len < maxlen) ? eof[fifon] : 0);
}
fifo->last_urblen = urb->actual_length;
status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { if (debug & DEBUG_HW)
printk(KERN_DEBUG "%s: %s: error resubmitting USB\n",
hw->name, __func__);
}
}
/* transmit completion routine for all ISO tx fifos */ staticvoid
tx_iso_complete(struct urb *urb)
{ struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; struct usb_fifo *fifo = context_iso_urb->owner_fifo; struct hfcsusb *hw = fifo->hw; struct sk_buff *tx_skb; int k, tx_offset, num_isoc_packets, sink, remain, current_len,
errcode, hdlc, i; int *tx_idx; int frame_complete, fifon, status, fillempty = 0;
__u8 threshbit, *p; unsignedlong flags;
/* * ISO transfer only partially completed, * look at individual frame status for details
*/ if (status == -EXDEV) { if (debug & DBG_HFC_URB_ERROR)
printk(KERN_DEBUG "%s: %s: " "-EXDEV (%i) fifon (%d)\n",
hw->name, __func__, status, fifon);
/* clear status, so go on with ISO transfers */
status = 0;
}
if (fifo->active && !status) { /* is FifoFull-threshold set for our channel? */
threshbit = (hw->threshold_mask & (1 << fifon));
num_isoc_packets = iso_packets[fifon];
/* USB data log for every D ISO out */ if ((fifon == HFCUSB_D_RX) && !fillempty &&
(debug & DBG_HFC_USB_VERBOSE)) {
printk(KERN_DEBUG "%s: %s (%d/%d) offs(%d) len(%d) ",
hw->name, __func__,
k, num_isoc_packets - 1,
urb->iso_frame_desc[k].offset,
urb->iso_frame_desc[k].length);
for (i = urb->iso_frame_desc[k].offset;
i < (urb->iso_frame_desc[k].offset
+ urb->iso_frame_desc[k].length);
i++)
printk("%x ",
context_iso_urb->buffer[i]);
/* * abuse DChannel tx iso completion to trigger NT mode state * changes tx_iso_complete is assumed to be called every * fifo->intervall (ms)
*/ if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0)
&& (hw->timers & NT_ACTIVATION_TIMER)) { if ((--hw->nt_timer) < 0)
schedule_event(&hw->dch, FLG_PHCHANGE);
}
/* * allocs urbs and start isoc transfer with two pending urbs to avoid * gaps in the transfer chain
*/ staticint
start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb,
usb_complete_t complete, int packet_size)
{ struct hfcsusb *hw = fifo->hw; int i, k, errcode;
if (debug)
printk(KERN_DEBUG "%s: %s: fifo %i\n",
hw->name, __func__, fifo->fifonum);
/* allocate Memory for Iso out Urbs */ for (i = 0; i < 2; i++) { if (!(fifo->iso[i].urb)) {
fifo->iso[i].urb =
usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); if (!(fifo->iso[i].urb)) {
printk(KERN_DEBUG "%s: %s: alloc urb for fifo %i failed",
hw->name, __func__, fifo->fifonum); continue;
}
fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo;
fifo->iso[i].indx = i;
/* Init the first iso */ if (ISO_BUFFER_SIZE >=
(fifo->usb_packet_maxlen *
num_packets_per_urb)) {
fill_isoc_urb(fifo->iso[i].urb,
fifo->hw->dev, fifo->pipe,
fifo->iso[i].buffer,
num_packets_per_urb,
fifo->usb_packet_maxlen,
fifo->intervall, complete,
&fifo->iso[i]);
memset(fifo->iso[i].buffer, 0, sizeof(fifo->iso[i].buffer));
for (k = 0; k < num_packets_per_urb; k++) {
fifo->iso[i].urb->
iso_frame_desc[k].offset =
k * packet_size;
fifo->iso[i].urb->
iso_frame_desc[k].length =
packet_size;
}
} else {
printk(KERN_DEBUG "%s: %s: ISO Buffer size to small!\n",
hw->name, __func__);
}
}
fifo->bit_line = BITLINE_INF;
staticvoid
reset_hfcsusb(struct hfcsusb *hw)
{ struct usb_fifo *fifo; int i;
if (debug & DEBUG_HW)
printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
/* do Chip reset */
write_reg(hw, HFCUSB_CIRM, 8);
/* aux = output, reset off */
write_reg(hw, HFCUSB_CIRM, 0x10);
/* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */
write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) |
((hw->packet_size / 8) << 4));
/* set USB_SIZE_I to match the wMaxPacketSize for ISO transfers */
write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size);
fifo = hw->fifos; for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
write_reg(hw, HFCUSB_FIFO, i); /* select the desired fifo */
fifo[i].max_size =
(i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN;
fifo[i].last_urblen = 0;
/* set 2 bit for D- & E-channel */
write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2));
/* enable all fifos */ if (i == HFCUSB_D_TX)
write_reg(hw, HFCUSB_CON_HDLC,
(hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09); else
write_reg(hw, HFCUSB_CON_HDLC, 0x08);
write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */
}
write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */
handle_led(hw, LED_POWER_ON);
}
/* start USB data pipes dependand on device's endpoint configuration */ staticvoid
hfcsusb_start_endpoint(struct hfcsusb *hw, int channel)
{ /* quick check if endpoint already running */ if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active)) return; if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active)) return; if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active)) return; if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active)) return;
/* start rx endpoints using USB INT IN method */ if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO)
start_int_fifo(hw->fifos + channel * 2 + 1);
/* start rx endpoints using USB ISO IN method */ if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) { switch (channel) { case HFC_CHAN_D:
start_isoc_chain(hw->fifos + HFCUSB_D_RX,
ISOC_PACKETS_D,
(usb_complete_t)rx_iso_complete,
16); break; case HFC_CHAN_E:
start_isoc_chain(hw->fifos + HFCUSB_PCM_RX,
ISOC_PACKETS_D,
(usb_complete_t)rx_iso_complete,
16); break; case HFC_CHAN_B1:
start_isoc_chain(hw->fifos + HFCUSB_B1_RX,
ISOC_PACKETS_B,
(usb_complete_t)rx_iso_complete,
16); break; case HFC_CHAN_B2:
start_isoc_chain(hw->fifos + HFCUSB_B2_RX,
ISOC_PACKETS_B,
(usb_complete_t)rx_iso_complete,
16); break;
}
}
/* start tx endpoints using USB ISO OUT method */ switch (channel) { case HFC_CHAN_D:
start_isoc_chain(hw->fifos + HFCUSB_D_TX,
ISOC_PACKETS_B,
(usb_complete_t)tx_iso_complete, 1); break; case HFC_CHAN_B1:
start_isoc_chain(hw->fifos + HFCUSB_B1_TX,
ISOC_PACKETS_D,
(usb_complete_t)tx_iso_complete, 1); break; case HFC_CHAN_B2:
start_isoc_chain(hw->fifos + HFCUSB_B2_TX,
ISOC_PACKETS_B,
(usb_complete_t)tx_iso_complete, 1); break;
}
}
/* stop USB data pipes dependand on device's endpoint configuration */ staticvoid
hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel)
{ /* quick check if endpoint currently running */ if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active)) return; if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active)) return; if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active)) return; if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active)) return;
/* rx endpoints using USB INT IN method */ if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO)
stop_int_gracefull(hw->fifos + channel * 2 + 1);
/* rx endpoints using USB ISO IN method */ if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO)
stop_iso_gracefull(hw->fifos + channel * 2 + 1);
/* tx endpoints using USB ISO OUT method */ if (channel != HFC_CHAN_E)
stop_iso_gracefull(hw->fifos + channel * 2);
}
if (vend_idx == 0xffff) {
printk(KERN_WARNING "%s: no valid vendor found in USB descriptor\n",
__func__); return -EIO;
} /* if vendor and product ID is OK, start probing alternate settings */
alt_idx = 0;
small_match = -1;
hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); if (!hw->ctrl_urb) {
pr_warn("%s: No memory for control urb\n",
driver_info->vend_name);
err = -ENOMEM; goto err_free_hw;
}
/* function called when an active device is removed */ staticvoid
hfcsusb_disconnect(struct usb_interface *intf)
{ struct hfcsusb *hw = usb_get_intfdata(intf); struct hfcsusb *next; int cnt = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.