mirror of
https://gitea.osmocom.org/sim-card/simtrace2.git
synced 2026-03-16 21:28:33 +03:00
Remove the paragraph about writing to the Free Software Foundation's mailing address. The FSF has changed addresses in the past, and may do so again. In 2021 this is not useful, let's rather have a bit less boilerplate at the start of source files. Change-Id: Ie0a3b2273383adbb3303faffd6ff96be7f4cae99
197 lines
4.8 KiB
C
197 lines
4.8 KiB
C
/* USB communication methods
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
#include "board.h"
|
|
#include "llist_irqsafe.h"
|
|
#include "usb_buf.h"
|
|
#include "utils.h"
|
|
|
|
#include <osmocom/core/linuxlist.h>
|
|
#include <osmocom/core/msgb.h>
|
|
#include <errno.h>
|
|
|
|
/***********************************************************************
|
|
* USBD Integration API
|
|
***********************************************************************/
|
|
|
|
/* call-back after (successful?) transfer of a write buffer on IN EP */
|
|
static void usb_write_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
|
|
uint32_t remaining)
|
|
{
|
|
struct msgb *msg = (struct msgb *) arg;
|
|
struct usb_buffered_ep *bep = msg->dst;
|
|
uint16_t ep_size = USBD_GetEndpointSize(bep->ep);
|
|
unsigned long x;
|
|
|
|
TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
|
|
|
|
if (((msgb_length(msg) % ep_size) == 0) && (transferred == ep_size)) {
|
|
/* terminate with ZLP; pass in 'msg' again as 'arg' so we get
|
|
* called the second time and proceed with usb_buf_free below */
|
|
USBD_Write(bep->ep, 0, 0, (TransferCallback) &usb_write_cb, msg);
|
|
return;
|
|
}
|
|
|
|
local_irq_save(x);
|
|
bep->in_progress--;
|
|
local_irq_restore(x);
|
|
TRACE_DEBUG("%u: in_progress=%lu\r\n", bep->ep, bep->in_progress);
|
|
|
|
if (status != USBD_STATUS_SUCCESS)
|
|
TRACE_ERROR("%s error, status=%d\r\n", __func__, status);
|
|
|
|
usb_buf_free(msg);
|
|
}
|
|
|
|
/* check if the spcified IN endpoint is idle and submit the next buffer from queue */
|
|
int usb_refill_to_host(uint8_t ep)
|
|
{
|
|
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
|
struct msgb *msg;
|
|
unsigned long x;
|
|
int rc;
|
|
|
|
#if 0
|
|
if (bep->out_from_host) {
|
|
TRACE_ERROR("EP 0x%02x is not IN\r\n", bep->ep);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
local_irq_save(x);
|
|
if (bep->in_progress) {
|
|
local_irq_restore(x);
|
|
return 0;
|
|
}
|
|
|
|
if (llist_empty(&bep->queue)) {
|
|
local_irq_restore(x);
|
|
return 0;
|
|
}
|
|
|
|
bep->in_progress++;
|
|
|
|
msg = msgb_dequeue_count(&bep->queue, &bep->queue_len);
|
|
|
|
local_irq_restore(x);
|
|
|
|
TRACE_DEBUG("%s (EP=0x%02x), in_progress=%lu\r\n", __func__, ep, bep->in_progress);
|
|
|
|
msg->dst = bep;
|
|
|
|
rc = USBD_Write(ep, msgb_data(msg), msgb_length(msg),
|
|
(TransferCallback) &usb_write_cb, msg);
|
|
if (rc != USBD_STATUS_SUCCESS) {
|
|
TRACE_ERROR("%s error %x\r\n", __func__, rc);
|
|
/* re-insert to head of queue */
|
|
llist_add_irqsafe(&msg->list, &bep->queue);
|
|
local_irq_save(x);
|
|
bep->in_progress--;
|
|
local_irq_restore(x);
|
|
TRACE_DEBUG("%02x: in_progress=%lu\r\n", bep->ep, bep->in_progress);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* call-back after (successful?) read transfer of a buffer on OUT EP */
|
|
static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
|
|
uint32_t remaining)
|
|
{
|
|
struct msgb *msg = (struct msgb *) arg;
|
|
struct usb_buffered_ep *bep = msg->dst;
|
|
|
|
TRACE_DEBUG("%s (EP=%u, len=%lu, q=%p)\r\n", __func__,
|
|
bep->ep, transferred, &bep->queue);
|
|
|
|
bep->in_progress = 0;
|
|
|
|
if (status != USBD_STATUS_SUCCESS) {
|
|
TRACE_ERROR("%s error, status=%d\r\n", __func__, status);
|
|
usb_buf_free(msg);
|
|
return;
|
|
}
|
|
msgb_put(msg, transferred);
|
|
llist_add_tail_irqsafe(&msg->list, &bep->queue);
|
|
}
|
|
|
|
/* refill the read queue for data received from host PC on OUT EP, if needed */
|
|
int usb_refill_from_host(uint8_t ep)
|
|
{
|
|
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
|
struct msgb *msg;
|
|
unsigned long x;
|
|
int rc;
|
|
|
|
#if 0
|
|
if (!bep->out_from_host) {
|
|
TRACE_ERROR("EP 0x%02x is not OUT\r\n", bep->ep);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
if (bep->in_progress)
|
|
return 0;
|
|
|
|
TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
|
|
|
|
msg = usb_buf_alloc(bep->ep);
|
|
if (!msg)
|
|
return -ENOMEM;
|
|
msg->dst = bep;
|
|
msg->l1h = msg->head;
|
|
|
|
bep->in_progress = 1;
|
|
|
|
rc = USBD_Read(ep, msg->head, msgb_tailroom(msg),
|
|
(TransferCallback) &usb_read_cb, msg);
|
|
if (rc != USBD_STATUS_SUCCESS) {
|
|
TRACE_ERROR("%s error %d\r\n", __func__, rc);
|
|
usb_buf_free(msg);
|
|
bep->in_progress = 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* drain any buffers from the queue of the endpoint and release their memory */
|
|
int usb_drain_queue(uint8_t ep)
|
|
{
|
|
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
|
struct msgb *msg;
|
|
unsigned long x;
|
|
int ret = 0;
|
|
|
|
/* wait until no transfers are in progress anymore and block
|
|
* further interrupts */
|
|
while (1) {
|
|
local_irq_save(x);
|
|
if (!bep->in_progress) {
|
|
break;
|
|
}
|
|
local_irq_restore(x);
|
|
/* retry */
|
|
}
|
|
|
|
/* free all queued msgbs */
|
|
while ((msg = msgb_dequeue_count(&bep->queue, &bep->queue_len))) {
|
|
usb_buf_free(msg);
|
|
ret++;
|
|
}
|
|
|
|
/* re-enable interrupts and return number of free'd msgbs */
|
|
local_irq_restore(x);
|
|
|
|
return ret;
|
|
}
|