-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathusb_dev.c
187 lines (151 loc) · 4.36 KB
/
usb_dev.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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 <linux/kthread.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/usb/ipc_bridge.h>
#include "qrtr.h"
#define IPC_DRIVER_NAME "ipc_bridge"
struct qrtr_usb_dev_ep {
struct qrtr_endpoint ep;
struct platform_device *pdev;
struct task_struct *rx_thread;
};
static struct qrtr_usb_dev_ep *qep;
/* from qrtr to usb */
static int qrtr_usb_dev_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
{
struct qrtr_usb_dev_ep *qep =
container_of(ep, struct qrtr_usb_dev_ep, ep);
struct ipc_bridge_platform_data *ipc_bridge;
int rc;
ipc_bridge = qep->pdev->dev.platform_data;
rc = skb_linearize(skb);
if (rc)
goto exit_free_skb;
rc = ipc_bridge->write(qep->pdev, skb->data, skb->len);
if (rc < 0) {
dev_err(&qep->pdev->dev, "error writing data %d\n", rc);
} else if (rc != skb->len) {
dev_err(&qep->pdev->dev, "wrote partial data, len=%d\n", rc);
rc = -EIO;
} else {
dev_dbg(&qep->pdev->dev, "wrote message with len=%d\n", rc);
rc = 0;
}
exit_free_skb:
if (rc)
kfree_skb(skb);
else
consume_skb(skb);
return rc;
}
/* from usb to qrtr */
static int qrtr_usb_dev_rx_thread_fn(void *data)
{
struct qrtr_usb_dev_ep *qep = data;
struct ipc_bridge_platform_data *ipc_bridge;
void *buf;
int bytes_read;
int rc = 0;
ipc_bridge = qep->pdev->dev.platform_data;
buf = kmalloc(ipc_bridge->max_read_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
while (!kthread_should_stop()) {
bytes_read = ipc_bridge->read(qep->pdev, buf,
ipc_bridge->max_read_size);
if (bytes_read < 0) {
dev_err(&qep->pdev->dev,
"error in ipc read operation %d\n", bytes_read);
continue;
}
dev_dbg(&qep->pdev->dev, "received message with len=%d\n",
bytes_read);
rc = qrtr_endpoint_post(&qep->ep, buf, bytes_read);
if (rc == -EINVAL)
dev_err(&qep->pdev->dev,
"invalid ipcrouter packet\n");
}
kfree(buf);
dev_dbg(&qep->pdev->dev, "leaving rx_thread\n");
return rc;
}
static int qrtr_usb_dev_probe(struct platform_device *pdev)
{
struct ipc_bridge_platform_data *ipc_bridge;
int rc;
ipc_bridge = pdev->dev.platform_data;
if (!ipc_bridge || !ipc_bridge->open || !ipc_bridge->read ||
!ipc_bridge->write || !ipc_bridge->close) {
dev_err(&pdev->dev,
"ipc_bridge or ipc_bridge->operations is NULL\n");
rc = -EINVAL;
goto exit;
}
rc = ipc_bridge->open(pdev);
if (rc) {
dev_err(&pdev->dev, "channel open failed for %s.%s\n",
pdev->name, pdev->id);
goto exit;
}
qep = devm_kzalloc(&pdev->dev, sizeof(*qep), GFP_KERNEL);
if (!qep) {
rc = -ENOMEM;
goto exit_close_bridge;
}
qep->pdev = pdev;
qep->ep.xmit = qrtr_usb_dev_send;
rc = qrtr_endpoint_register(&qep->ep, QRTR_EP_NID_AUTO, false);
if (rc) {
dev_err(&pdev->dev, "failed to register qrtr endpoint\n");
goto exit_close_bridge;
}
qep->rx_thread = kthread_run(qrtr_usb_dev_rx_thread_fn, qep,
"qrtr-usb-dev-rx");
if (IS_ERR(qep->rx_thread)) {
dev_err(&qep->pdev->dev, "could not create rx_thread\n");
rc = PTR_ERR(qep->rx_thread);
goto exit_qrtr_unregister;
}
dev_dbg(&qep->pdev->dev, "QTI USB-dev QRTR driver probed\n");
return 0;
exit_qrtr_unregister:
qrtr_endpoint_unregister(&qep->ep);
exit_close_bridge:
ipc_bridge->close(pdev);
exit:
return rc;
}
static int qrtr_usb_dev_remove(struct platform_device *pdev)
{
struct ipc_bridge_platform_data *ipc_bridge;
dev_dbg(&pdev->dev, "removing the platform dev\n");
kthread_stop(qep->rx_thread);
qrtr_endpoint_unregister(&qep->ep);
ipc_bridge = pdev->dev.platform_data;
if (ipc_bridge && ipc_bridge->close)
ipc_bridge->close(pdev);
return 0;
}
static struct platform_driver qrtr_usb_dev_driver = {
.probe = qrtr_usb_dev_probe,
.remove = qrtr_usb_dev_remove,
.driver = {
.name = IPC_DRIVER_NAME,
.owner = THIS_MODULE,
},
};
module_platform_driver(qrtr_usb_dev_driver);
MODULE_DESCRIPTION("QTI IPC-Router USB device interface driver");
MODULE_LICENSE("GPL v2");