diff -uprN -X /usr/src/dontdiff usbatm-orig/usbatm.c usbatm/usbatm.c --- usbatm-orig/usbatm.c 2005-11-08 10:58:29.000000000 +0100 +++ usbatm/usbatm.c 2005-11-08 23:15:28.000000000 +0100 @@ -293,38 +293,30 @@ static inline struct usbatm_vcc_data *us return NULL; } -static void usbatm_extract_cells(struct usbatm_data *instance, - unsigned char *source, unsigned int avail_data) +static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char *source) { - struct usbatm_vcc_data *cached_vcc = NULL; struct atm_vcc *vcc; struct sk_buff *sarb; - unsigned int stride = instance->rx_channel.stride; - int vci, cached_vci = 0; - short vpi, cached_vpi = 0; - u8 pti; - - for (; avail_data >= stride; avail_data -= stride, source += stride) { - vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4); - vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); - pti = ((source[3] & 0xe) >> 1); - + short vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4); + int vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); + u8 pti = ((source[3] & 0xe) >> 1); + vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti); - if ((vci != cached_vci) || (vpi != cached_vpi)) { - cached_vpi = vpi; - cached_vci = vci; + if ((vci != instance->cached_vci) || (vpi != instance->cached_vpi)) { + instance->cached_vpi = vpi; + instance->cached_vci = vci; - cached_vcc = usbatm_find_vcc(instance, vpi, vci); + instance->cached_vcc = usbatm_find_vcc(instance, vpi, vci); - if (!cached_vcc) + if (!instance->cached_vcc) atm_rldbg(instance, "%s: unknown vpi/vci (%hd/%d)!\n", __func__, vpi, vci); } - if (!cached_vcc) - continue; + if (!instance->cached_vcc) + return; - vcc = cached_vcc->vcc; + vcc = instance->cached_vcc->vcc; /* OAM F5 end-to-end */ if (pti == ATM_PTI_E2EF5) { @@ -332,10 +324,10 @@ static void usbatm_extract_cells(struct atm_warn(instance, "%s: OAM not supported (vpi %d, vci %d)!\n", __func__, vpi, vci); atomic_inc(&vcc->stats->rx_err); - continue; + return; } - sarb = cached_vcc->sarb; + sarb = instance->cached_vcc->sarb; if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { atm_rldbg(instance, "%s: buffer overrun (sarb->len %u, vcc: 0x%p)!\n", @@ -412,6 +404,45 @@ static void usbatm_extract_cells(struct out: skb_trim(sarb, 0); } +} + +static void usbatm_extract_cells(struct usbatm_data *instance, + unsigned char *source, unsigned int avail_data) +{ + unsigned int stride = instance->rx_channel.stride; + unsigned int cell_len = instance->cell_len; + + /* extract cells from incomming data, take into account + * length of avail data may not be multiple of stride */ + + if (cell_len > 0) { + /* we have partially received atm cell */ + unsigned int remain_data = stride - cell_len; + unsigned char *cell_buf = instance->cell_buf; + + if (avail_data >= remain_data) { + /* add new data and process cell */ + memcpy(cell_buf + cell_len, source, remain_data); + source += remain_data; + avail_data -= remain_data; + usbatm_extract_one_cell(instance, cell_buf); + instance->cell_len = 0; + } else { + /* too small data chunk to fill cell */ + memcpy(cell_buf + cell_len, source, avail_data); + instance->cell_len = cell_len + avail_data; + return; + } + } + + for (; avail_data >= stride; avail_data -= stride, source += stride) + usbatm_extract_one_cell(instance, source); + + if (avail_data > 0) { + /* length was not multiple of stride, + * save remain data to next call */ + memcpy(instance->cell_buf, source, avail_data); + instance->cell_len = avail_data; } } @@ -794,6 +825,10 @@ static int usbatm_atm_open(struct atm_vc vcc->dev_data = new; tasklet_disable(&instance->rx_channel.tasklet); + instance->cached_vcc = new; + instance->cached_vpi = vpi; + instance->cached_vci = vci; + instance->cell_len = 0; list_add(&new->list, &instance->vcc_list); tasklet_enable(&instance->rx_channel.tasklet); @@ -833,6 +868,10 @@ static void usbatm_atm_close(struct atm_ down(&instance->serialize); /* vs self, usbatm_atm_open, usbatm_usb_disconnect */ tasklet_disable(&instance->rx_channel.tasklet); + instance->cached_vcc = NULL; + instance->cached_vpi = ATM_VPI_UNSPEC; + instance->cached_vci = ATM_VCI_UNSPEC; + instance->cell_len = 0; list_del(&vcc_data->list); tasklet_enable(&instance->rx_channel.tasklet); @@ -965,7 +1004,17 @@ static int usbatm_heavy_init(struct usba static void usbatm_tasklet_schedule(unsigned long data) { - tasklet_schedule((struct tasklet_struct *) data); + struct usbatm_channel *channel = (struct usbatm_channel *) data; + + if (usb_pipein(channel->endpoint)) { + /* when some error occures on urb + * don't use old incoming atm cells order */ + tasklet_disable(&channel->tasklet); + channel->usbatm->cell_len = 0; + tasklet_enable(&channel->tasklet); + } + + tasklet_schedule(&channel->tasklet); } static inline void usbatm_init_channel(struct usbatm_channel *channel) @@ -973,7 +1022,7 @@ static inline void usbatm_init_channel(s spin_lock_init(&channel->lock); INIT_LIST_HEAD(&channel->list); channel->delay.function = usbatm_tasklet_schedule; - channel->delay.data = (unsigned long) &channel->tasklet; + channel->delay.data = (unsigned long) &channel; init_timer(&channel->delay); } @@ -1068,6 +1117,16 @@ int usbatm_usb_probe(struct usb_interfac skb_queue_head_init(&instance->sndqueue); + instance->cached_vpi = ATM_VPI_UNSPEC; + instance->cached_vci = ATM_VCI_UNSPEC; + instance->cell_buf = kmalloc(instance->rx_channel.stride, GFP_KERNEL); + + if (!instance->cell_buf) { + dev_dbg(dev, "%s: no memory for cell buffer\n", __func__); + error = -ENOMEM; + goto fail_unbind; + } + for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) { struct urb *urb; u8 *buffer; diff -uprN -X /usr/src/dontdiff usbatm-orig/usbatm.h usbatm/usbatm.h --- usbatm-orig/usbatm.h 2005-11-08 10:58:29.000000000 +0100 +++ usbatm/usbatm.h 2005-11-08 23:05:52.000000000 +0100 @@ -190,6 +190,13 @@ struct usbatm_data { struct sk_buff_head sndqueue; struct sk_buff *current_skb; /* being emptied */ + struct usbatm_vcc_data *cached_vcc; + int cached_vci; + short cached_vpi; + + unsigned char *cell_buf; + unsigned int cell_len; + struct urb *urbs[0]; };