blob: acf931ea09310ed5b61f18a798212ebe58db03a4 [file] [log] [blame]
Harald Welte8e7fca32017-05-07 16:14:33 +02001#include "board.h"
2#include "llist_irqsafe.h"
3#include "usb_buf.h"
Harald Welte7abdb512016-03-03 17:48:32 +01004
Harald Welte8e7fca32017-05-07 16:14:33 +02005#include "osmocom/core/linuxlist.h"
6#include "osmocom/core/msgb.h"
Harald Welte3d27c842016-03-17 20:07:45 +01007#include <errno.h>
8
Harald Welte8e7fca32017-05-07 16:14:33 +02009/***********************************************************************
10 * USBD Integration API
11 ***********************************************************************/
Christina Quast2b8a18b2015-04-12 09:31:36 +020012
Harald Welte2a6d3af2016-02-28 19:29:14 +010013/* call-back after (successful?) transfer of a buffer */
14static void usb_write_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
15 uint32_t remaining)
Christina Quast2b8a18b2015-04-12 09:31:36 +020016{
Harald Welte8e7fca32017-05-07 16:14:33 +020017 struct msgb *msg = (struct msgb *) arg;
18 struct usb_buffered_ep *bep = msg->dst;
Harald Welte2a6d3af2016-02-28 19:29:14 +010019
Harald Welte8e7fca32017-05-07 16:14:33 +020020 TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
Harald Weltea0cf2002016-03-02 10:35:51 +010021
22 __disable_irq();
Harald Welte8e7fca32017-05-07 16:14:33 +020023 bep->in_progress--;
Harald Weltea0cf2002016-03-02 10:35:51 +010024 __enable_irq();
Harald Welte8e7fca32017-05-07 16:14:33 +020025 TRACE_DEBUG("%u: in_progress=%d\n", bep->ep, bep->in_progress);
Harald Welte54cb3d02016-02-29 14:12:40 +010026
Harald Welte2a6d3af2016-02-28 19:29:14 +010027 if (status != USBD_STATUS_SUCCESS)
28 TRACE_ERROR("%s error, status=%d\n", __func__, status);
29
Harald Welte8e7fca32017-05-07 16:14:33 +020030 usb_buf_free(msg);
Christina Quast2b8a18b2015-04-12 09:31:36 +020031}
32
Harald Welte8e7fca32017-05-07 16:14:33 +020033int usb_refill_to_host(uint8_t ep)
Christina Quast2b8a18b2015-04-12 09:31:36 +020034{
Harald Welte8e7fca32017-05-07 16:14:33 +020035 struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
36 struct msgb *msg;
Harald Welte2a6d3af2016-02-28 19:29:14 +010037 int rc;
Christina Quast2b8a18b2015-04-12 09:31:36 +020038
Harald Welte8e7fca32017-05-07 16:14:33 +020039#if 0
40 if (bep->out_from_host) {
41 TRACE_ERROR("EP 0x%02x is not IN\r\n", bep->ep);
42 return -EINVAL;
43 }
44#endif
45
Harald Weltea0cf2002016-03-02 10:35:51 +010046 __disable_irq();
Harald Welte8e7fca32017-05-07 16:14:33 +020047 if (bep->in_progress) {
Harald Weltea0cf2002016-03-02 10:35:51 +010048 __enable_irq();
Harald Welte54cb3d02016-02-29 14:12:40 +010049 return 0;
Harald Weltea0cf2002016-03-02 10:35:51 +010050 }
Harald Welte2a6d3af2016-02-28 19:29:14 +010051
Harald Welte8e7fca32017-05-07 16:14:33 +020052 if (llist_empty(&bep->queue)) {
Harald Weltea0cf2002016-03-02 10:35:51 +010053 __enable_irq();
Harald Welte54cb3d02016-02-29 14:12:40 +010054 return 0;
Harald Weltea0cf2002016-03-02 10:35:51 +010055 }
56
Harald Welte8e7fca32017-05-07 16:14:33 +020057 bep->in_progress++;
Harald Welte54cb3d02016-02-29 14:12:40 +010058
Harald Welte8e7fca32017-05-07 16:14:33 +020059 msg = msgb_dequeue(&bep->queue);
Harald Welte54cb3d02016-02-29 14:12:40 +010060
Harald Weltea0cf2002016-03-02 10:35:51 +010061 __enable_irq();
62
Harald Welte8e7fca32017-05-07 16:14:33 +020063 TRACE_DEBUG("%s (EP=0x%02x), in_progress=%d\r\n", __func__, ep, bep->in_progress);
Harald Weltea0cf2002016-03-02 10:35:51 +010064
Harald Welte8e7fca32017-05-07 16:14:33 +020065 msg->dst = bep;
Harald Welte54cb3d02016-02-29 14:12:40 +010066
Harald Welte8e7fca32017-05-07 16:14:33 +020067 rc = USBD_Write(ep, msgb_data(msg), msgb_length(msg),
68 (TransferCallback) &usb_write_cb, msg);
Harald Welte2a6d3af2016-02-28 19:29:14 +010069 if (rc != USBD_STATUS_SUCCESS) {
Harald Welte2ad0ca12016-02-29 10:07:16 +010070 TRACE_ERROR("%s error %x\n", __func__, rc);
Harald Welte8e7fca32017-05-07 16:14:33 +020071 /* re-insert to head of queue */
72 llist_add_irqsafe(&msg->list, &bep->queue);
Harald Weltea0cf2002016-03-02 10:35:51 +010073 __disable_irq();
Harald Welte8e7fca32017-05-07 16:14:33 +020074 bep->in_progress--;
Harald Weltea0cf2002016-03-02 10:35:51 +010075 __enable_irq();
Harald Welte8e7fca32017-05-07 16:14:33 +020076 TRACE_DEBUG("%02x: in_progress=%d\n", bep->ep, bep->in_progress);
Harald Welte2a6d3af2016-02-28 19:29:14 +010077 return 0;
78 }
79
80 return 1;
Christina Quast2b8a18b2015-04-12 09:31:36 +020081}
82
Harald Welte8e7fca32017-05-07 16:14:33 +020083/* call-back after (successful?) transfer of a buffer */
Harald Welte2a6d3af2016-02-28 19:29:14 +010084static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
85 uint32_t remaining)
Christina Quast2b8a18b2015-04-12 09:31:36 +020086{
Harald Welte8e7fca32017-05-07 16:14:33 +020087 struct msgb *msg = (struct msgb *) arg;
88 struct usb_buffered_ep *bep = msg->dst;
Harald Welte54cb3d02016-02-29 14:12:40 +010089
Harald Welte53079bb2016-03-20 14:58:35 +010090 TRACE_DEBUG("%s (EP=%u, len=%u, q=%p)\r\n", __func__,
Harald Welte8e7fca32017-05-07 16:14:33 +020091 bep->ep, transferred, &bep->queue);
Harald Weltea0cf2002016-03-02 10:35:51 +010092
Harald Welte8e7fca32017-05-07 16:14:33 +020093 bep->in_progress = 0;
Christina Quast2b8a18b2015-04-12 09:31:36 +020094
Harald Welte2a6d3af2016-02-28 19:29:14 +010095 if (status != USBD_STATUS_SUCCESS) {
96 TRACE_ERROR("%s error, status=%d\n", __func__, status);
Harald Welte8e7fca32017-05-07 16:14:33 +020097 usb_buf_free(msg);
Harald Welte2a6d3af2016-02-28 19:29:14 +010098 return;
99 }
Harald Welte8e7fca32017-05-07 16:14:33 +0200100 msgb_put(msg, transferred);
101 llist_add_tail_irqsafe(&msg->list, &bep->queue);
Harald Welte2a6d3af2016-02-28 19:29:14 +0100102}
103
Harald Welte8e7fca32017-05-07 16:14:33 +0200104int usb_refill_from_host(uint8_t ep)
Harald Welte2a6d3af2016-02-28 19:29:14 +0100105{
Harald Welte8e7fca32017-05-07 16:14:33 +0200106 struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
107 struct msgb *msg;
Harald Welte2a6d3af2016-02-28 19:29:14 +0100108 int rc;
109
Harald Welte8e7fca32017-05-07 16:14:33 +0200110#if 0
111 if (!bep->out_from_host) {
112 TRACE_ERROR("EP 0x%02x is not OUT\r\n", bep->ep);
113 return -EINVAL;
114 }
115#endif
116
117 if (bep->in_progress)
Harald Welte54cb3d02016-02-29 14:12:40 +0100118 return 0;
119
Harald Welte8e7fca32017-05-07 16:14:33 +0200120 TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
Harald Weltea0cf2002016-03-02 10:35:51 +0100121
Harald Welte8e7fca32017-05-07 16:14:33 +0200122 msg = usb_buf_alloc(bep->ep);
123 if (!msg)
Harald Welte3d27c842016-03-17 20:07:45 +0100124 return -ENOMEM;
Harald Welte8e7fca32017-05-07 16:14:33 +0200125 msg->dst = bep;
126 msg->l1h = msg->head;
Harald Welte3d27c842016-03-17 20:07:45 +0100127
Harald Welte8e7fca32017-05-07 16:14:33 +0200128 bep->in_progress = 1;
Harald Welte2a6d3af2016-02-28 19:29:14 +0100129
Harald Welte8e7fca32017-05-07 16:14:33 +0200130 rc = USBD_Read(ep, msg->head, msgb_tailroom(msg),
131 (TransferCallback) &usb_read_cb, msg);
Harald Welte2a6d3af2016-02-28 19:29:14 +0100132 if (rc != USBD_STATUS_SUCCESS) {
Harald Welte8e7fca32017-05-07 16:14:33 +0200133 TRACE_ERROR("%s error %s\n", __func__, rc);
134 usb_buf_free(msg);
135 bep->in_progress = 0;
Harald Welte2a6d3af2016-02-28 19:29:14 +0100136 }
137
Harald Welte54cb3d02016-02-29 14:12:40 +0100138 return 1;
Christina Quast2b8a18b2015-04-12 09:31:36 +0200139}
Harald Welte8e7fca32017-05-07 16:14:33 +0200140
141int usb_drain_queue(uint8_t ep)
142{
143 struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
144 struct msgb *msg;
145 int ret = 0;
146
147 /* wait until no transfers are in progress anymore and block
148 * further interrupts */
149 while (1) {
150 __disable_irq();
151 if (!bep->in_progress) {
152 break;
153 }
154 __enable_irq();
155 /* retry */
156 }
157
158 /* free all queued msgbs */
159 while ((msg = msgb_dequeue(&bep->queue))) {
160 usb_buf_free(msg);
161 ret++;
162 }
163
164 /* re-enable interrupts and return number of free'd msgbs */
165 __enable_irq();
166
167 return ret;
168}