blob: 9a6e5b327d7006f85743bc3b1ee0d26070b86ad4 [file] [log] [blame]
Kévin Redon9a12d682018-07-08 13:21:16 +02001/* USB communication methods
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
16 */
Harald Welte8e7fca32017-05-07 16:14:33 +020017#include "board.h"
18#include "llist_irqsafe.h"
19#include "usb_buf.h"
Harald Welte119624f2017-11-04 12:28:30 +010020#include "utils.h"
Harald Welte7abdb512016-03-03 17:48:32 +010021
Harald Welte9d90d282018-06-29 22:25:42 +020022#include <osmocom/core/linuxlist.h>
23#include <osmocom/core/msgb.h>
Harald Welte3d27c842016-03-17 20:07:45 +010024#include <errno.h>
25
Harald Welte8e7fca32017-05-07 16:14:33 +020026/***********************************************************************
27 * USBD Integration API
28 ***********************************************************************/
Christina Quast2b8a18b2015-04-12 09:31:36 +020029
Harald Welte271be9d2019-12-14 18:58:58 +010030/* call-back after (successful?) transfer of a write buffer on IN EP */
Harald Welte2a6d3af2016-02-28 19:29:14 +010031static void usb_write_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
32 uint32_t remaining)
Christina Quast2b8a18b2015-04-12 09:31:36 +020033{
Harald Welte8e7fca32017-05-07 16:14:33 +020034 struct msgb *msg = (struct msgb *) arg;
35 struct usb_buffered_ep *bep = msg->dst;
Harald Welte5b136022019-12-15 23:55:15 +010036 uint16_t ep_size = USBD_GetEndpointSize(bep->ep);
Harald Welte119624f2017-11-04 12:28:30 +010037 unsigned long x;
Harald Welte2a6d3af2016-02-28 19:29:14 +010038
Harald Welte8e7fca32017-05-07 16:14:33 +020039 TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
Harald Weltea0cf2002016-03-02 10:35:51 +010040
Harald Welte5b136022019-12-15 23:55:15 +010041 if (((msgb_length(msg) % ep_size) == 0) && (transferred == ep_size)) {
42 /* terminate with ZLP; pass in 'msg' again as 'arg' so we get
43 * called the second time and proceed with usb_buf_free below */
44 USBD_Write(bep->ep, 0, 0, (TransferCallback) &usb_write_cb, msg);
45 return;
46 }
47
Harald Welte119624f2017-11-04 12:28:30 +010048 local_irq_save(x);
Harald Welte8e7fca32017-05-07 16:14:33 +020049 bep->in_progress--;
Harald Welte119624f2017-11-04 12:28:30 +010050 local_irq_restore(x);
Harald Welte342a7fe2019-12-14 11:53:46 +010051 TRACE_DEBUG("%u: in_progress=%lu\r\n", bep->ep, bep->in_progress);
Harald Welte54cb3d02016-02-29 14:12:40 +010052
Harald Welte2a6d3af2016-02-28 19:29:14 +010053 if (status != USBD_STATUS_SUCCESS)
Harald Welte342a7fe2019-12-14 11:53:46 +010054 TRACE_ERROR("%s error, status=%d\r\n", __func__, status);
Harald Welte2a6d3af2016-02-28 19:29:14 +010055
Harald Welte8e7fca32017-05-07 16:14:33 +020056 usb_buf_free(msg);
Christina Quast2b8a18b2015-04-12 09:31:36 +020057}
58
Harald Welte271be9d2019-12-14 18:58:58 +010059/* check if the spcified IN endpoint is idle and submit the next buffer from queue */
Harald Welte8e7fca32017-05-07 16:14:33 +020060int usb_refill_to_host(uint8_t ep)
Christina Quast2b8a18b2015-04-12 09:31:36 +020061{
Harald Welte8e7fca32017-05-07 16:14:33 +020062 struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
63 struct msgb *msg;
Harald Welte119624f2017-11-04 12:28:30 +010064 unsigned long x;
Harald Welte2a6d3af2016-02-28 19:29:14 +010065 int rc;
Christina Quast2b8a18b2015-04-12 09:31:36 +020066
Harald Welte8e7fca32017-05-07 16:14:33 +020067#if 0
68 if (bep->out_from_host) {
69 TRACE_ERROR("EP 0x%02x is not IN\r\n", bep->ep);
70 return -EINVAL;
71 }
72#endif
73
Harald Welte119624f2017-11-04 12:28:30 +010074 local_irq_save(x);
Harald Welte8e7fca32017-05-07 16:14:33 +020075 if (bep->in_progress) {
Harald Welte119624f2017-11-04 12:28:30 +010076 local_irq_restore(x);
Harald Welte54cb3d02016-02-29 14:12:40 +010077 return 0;
Harald Weltea0cf2002016-03-02 10:35:51 +010078 }
Harald Welte2a6d3af2016-02-28 19:29:14 +010079
Harald Welte8e7fca32017-05-07 16:14:33 +020080 if (llist_empty(&bep->queue)) {
Harald Welte119624f2017-11-04 12:28:30 +010081 local_irq_restore(x);
Harald Welte54cb3d02016-02-29 14:12:40 +010082 return 0;
Harald Weltea0cf2002016-03-02 10:35:51 +010083 }
84
Harald Welte8e7fca32017-05-07 16:14:33 +020085 bep->in_progress++;
Harald Welte54cb3d02016-02-29 14:12:40 +010086
Harald Weltef4a625b2019-12-14 19:07:57 +010087 msg = msgb_dequeue_count(&bep->queue, &bep->queue_len);
Harald Welte54cb3d02016-02-29 14:12:40 +010088
Harald Welte119624f2017-11-04 12:28:30 +010089 local_irq_restore(x);
Harald Weltea0cf2002016-03-02 10:35:51 +010090
Harald Welte3c003cc2019-12-14 11:39:14 +010091 TRACE_DEBUG("%s (EP=0x%02x), in_progress=%lu\r\n", __func__, ep, bep->in_progress);
Harald Weltea0cf2002016-03-02 10:35:51 +010092
Harald Welte8e7fca32017-05-07 16:14:33 +020093 msg->dst = bep;
Harald Welte54cb3d02016-02-29 14:12:40 +010094
Harald Welte8e7fca32017-05-07 16:14:33 +020095 rc = USBD_Write(ep, msgb_data(msg), msgb_length(msg),
96 (TransferCallback) &usb_write_cb, msg);
Harald Welte2a6d3af2016-02-28 19:29:14 +010097 if (rc != USBD_STATUS_SUCCESS) {
Harald Welte342a7fe2019-12-14 11:53:46 +010098 TRACE_ERROR("%s error %x\r\n", __func__, rc);
Harald Welte8e7fca32017-05-07 16:14:33 +020099 /* re-insert to head of queue */
100 llist_add_irqsafe(&msg->list, &bep->queue);
Harald Welte119624f2017-11-04 12:28:30 +0100101 local_irq_save(x);
Harald Welte8e7fca32017-05-07 16:14:33 +0200102 bep->in_progress--;
Harald Welte119624f2017-11-04 12:28:30 +0100103 local_irq_restore(x);
Harald Welte342a7fe2019-12-14 11:53:46 +0100104 TRACE_DEBUG("%02x: in_progress=%lu\r\n", bep->ep, bep->in_progress);
Harald Welte2a6d3af2016-02-28 19:29:14 +0100105 return 0;
106 }
107
108 return 1;
Christina Quast2b8a18b2015-04-12 09:31:36 +0200109}
110
Harald Welte271be9d2019-12-14 18:58:58 +0100111/* call-back after (successful?) read transfer of a buffer on OUT EP */
Harald Welte2a6d3af2016-02-28 19:29:14 +0100112static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
113 uint32_t remaining)
Christina Quast2b8a18b2015-04-12 09:31:36 +0200114{
Harald Welte8e7fca32017-05-07 16:14:33 +0200115 struct msgb *msg = (struct msgb *) arg;
116 struct usb_buffered_ep *bep = msg->dst;
Harald Welte54cb3d02016-02-29 14:12:40 +0100117
Harald Welte3c003cc2019-12-14 11:39:14 +0100118 TRACE_DEBUG("%s (EP=%u, len=%lu, q=%p)\r\n", __func__,
Harald Welte8e7fca32017-05-07 16:14:33 +0200119 bep->ep, transferred, &bep->queue);
Harald Weltea0cf2002016-03-02 10:35:51 +0100120
Harald Welte8e7fca32017-05-07 16:14:33 +0200121 bep->in_progress = 0;
Christina Quast2b8a18b2015-04-12 09:31:36 +0200122
Harald Welte2a6d3af2016-02-28 19:29:14 +0100123 if (status != USBD_STATUS_SUCCESS) {
Harald Welte342a7fe2019-12-14 11:53:46 +0100124 TRACE_ERROR("%s error, status=%d\r\n", __func__, status);
Harald Welte8e7fca32017-05-07 16:14:33 +0200125 usb_buf_free(msg);
Harald Welte2a6d3af2016-02-28 19:29:14 +0100126 return;
127 }
Harald Welte8e7fca32017-05-07 16:14:33 +0200128 msgb_put(msg, transferred);
129 llist_add_tail_irqsafe(&msg->list, &bep->queue);
Harald Welte2a6d3af2016-02-28 19:29:14 +0100130}
131
Harald Welte271be9d2019-12-14 18:58:58 +0100132/* refill the read queue for data received from host PC on OUT EP, if needed */
Harald Welte8e7fca32017-05-07 16:14:33 +0200133int usb_refill_from_host(uint8_t ep)
Harald Welte2a6d3af2016-02-28 19:29:14 +0100134{
Harald Welte8e7fca32017-05-07 16:14:33 +0200135 struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
136 struct msgb *msg;
Harald Welte119624f2017-11-04 12:28:30 +0100137 unsigned long x;
Harald Welte2a6d3af2016-02-28 19:29:14 +0100138 int rc;
139
Harald Welte8e7fca32017-05-07 16:14:33 +0200140#if 0
141 if (!bep->out_from_host) {
142 TRACE_ERROR("EP 0x%02x is not OUT\r\n", bep->ep);
143 return -EINVAL;
144 }
145#endif
146
147 if (bep->in_progress)
Harald Welte54cb3d02016-02-29 14:12:40 +0100148 return 0;
149
Harald Welte8e7fca32017-05-07 16:14:33 +0200150 TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
Harald Weltea0cf2002016-03-02 10:35:51 +0100151
Harald Welte8e7fca32017-05-07 16:14:33 +0200152 msg = usb_buf_alloc(bep->ep);
153 if (!msg)
Harald Welte3d27c842016-03-17 20:07:45 +0100154 return -ENOMEM;
Harald Welte8e7fca32017-05-07 16:14:33 +0200155 msg->dst = bep;
156 msg->l1h = msg->head;
Harald Welte3d27c842016-03-17 20:07:45 +0100157
Harald Welte8e7fca32017-05-07 16:14:33 +0200158 bep->in_progress = 1;
Harald Welte2a6d3af2016-02-28 19:29:14 +0100159
Harald Welte8e7fca32017-05-07 16:14:33 +0200160 rc = USBD_Read(ep, msg->head, msgb_tailroom(msg),
161 (TransferCallback) &usb_read_cb, msg);
Harald Welte2a6d3af2016-02-28 19:29:14 +0100162 if (rc != USBD_STATUS_SUCCESS) {
Harald Welte342a7fe2019-12-14 11:53:46 +0100163 TRACE_ERROR("%s error %d\r\n", __func__, rc);
Harald Welte8e7fca32017-05-07 16:14:33 +0200164 usb_buf_free(msg);
165 bep->in_progress = 0;
Harald Welte2a6d3af2016-02-28 19:29:14 +0100166 }
167
Harald Welte54cb3d02016-02-29 14:12:40 +0100168 return 1;
Christina Quast2b8a18b2015-04-12 09:31:36 +0200169}
Harald Welte8e7fca32017-05-07 16:14:33 +0200170
Harald Welte271be9d2019-12-14 18:58:58 +0100171/* drain any buffers from the queue of the endpoint and release their memory */
Harald Welte8e7fca32017-05-07 16:14:33 +0200172int usb_drain_queue(uint8_t ep)
173{
174 struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
175 struct msgb *msg;
Harald Welte119624f2017-11-04 12:28:30 +0100176 unsigned long x;
Harald Welte8e7fca32017-05-07 16:14:33 +0200177 int ret = 0;
178
179 /* wait until no transfers are in progress anymore and block
180 * further interrupts */
181 while (1) {
Harald Welte119624f2017-11-04 12:28:30 +0100182 local_irq_save(x);
Harald Welte8e7fca32017-05-07 16:14:33 +0200183 if (!bep->in_progress) {
184 break;
185 }
Harald Welte119624f2017-11-04 12:28:30 +0100186 local_irq_restore(x);
Harald Welte8e7fca32017-05-07 16:14:33 +0200187 /* retry */
188 }
189
190 /* free all queued msgbs */
Harald Weltef4a625b2019-12-14 19:07:57 +0100191 while ((msg = msgb_dequeue_count(&bep->queue, &bep->queue_len))) {
Harald Welte8e7fca32017-05-07 16:14:33 +0200192 usb_buf_free(msg);
193 ret++;
194 }
195
196 /* re-enable interrupts and return number of free'd msgbs */
Harald Welte119624f2017-11-04 12:28:30 +0100197 local_irq_restore(x);
Harald Welte8e7fca32017-05-07 16:14:33 +0200198
199 return ret;
200}