blob: 51fca8e898a8b3742f23e8c88f9a45c38ad20d26 [file] [log] [blame]
Paul Cercueile06f4ce2015-04-20 12:59:31 +02001/*
2 * libiio - Library for interfacing industrial I/O (IIO) devices
3 *
4 * Copyright (C) 2015 Analog Devices, Inc.
5 * Author: Paul Cercueil <paul.cercueil@analog.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * */
18
19#include "debug.h"
20#include "iio-lock.h"
21#include "iio-private.h"
22#include "iiod-client.h"
23
24#include <errno.h>
25#include <libusb-1.0/libusb.h>
26#include <stdbool.h>
27#include <string.h>
28
29#define DEFAULT_TIMEOUT_MS 5000
30
31/* Endpoint for non-streaming operations */
32#define EP_OPS 1
33
34struct iio_context_pdata {
35 libusb_context *ctx;
36 libusb_device_handle *hdl;
37
38 struct iiod_client *iiod_client;
39
40 /* Lock for non-streaming operations */
41 struct iio_mutex *lock;
42};
43
44static const unsigned int libusb_to_errno_codes[] = {
45 [- LIBUSB_ERROR_INVALID_PARAM] = EINVAL,
46 [- LIBUSB_ERROR_ACCESS] = EACCES,
47 [- LIBUSB_ERROR_NO_DEVICE] = ENODEV,
48 [- LIBUSB_ERROR_NOT_FOUND] = ENXIO,
49 [- LIBUSB_ERROR_BUSY] = EBUSY,
50 [- LIBUSB_ERROR_TIMEOUT] = ETIMEDOUT,
51 [- LIBUSB_ERROR_OVERFLOW] = EIO,
52 [- LIBUSB_ERROR_PIPE] = EPIPE,
53 [- LIBUSB_ERROR_INTERRUPTED] = EINTR,
54 [- LIBUSB_ERROR_NO_MEM] = ENOMEM,
55 [- LIBUSB_ERROR_NOT_SUPPORTED] = ENOSYS,
56};
57
58static unsigned int libusb_to_errno(int error)
59{
60 switch ((enum libusb_error) error) {
61 case LIBUSB_ERROR_INVALID_PARAM:
62 case LIBUSB_ERROR_ACCESS:
63 case LIBUSB_ERROR_NO_DEVICE:
64 case LIBUSB_ERROR_NOT_FOUND:
65 case LIBUSB_ERROR_BUSY:
66 case LIBUSB_ERROR_TIMEOUT:
67 case LIBUSB_ERROR_PIPE:
68 case LIBUSB_ERROR_INTERRUPTED:
69 case LIBUSB_ERROR_NO_MEM:
70 case LIBUSB_ERROR_NOT_SUPPORTED:
71 return libusb_to_errno_codes[- (int) error];
72 case LIBUSB_ERROR_IO:
73 case LIBUSB_ERROR_OTHER:
74 case LIBUSB_ERROR_OVERFLOW:
75 default:
76 return EIO;
77 }
78}
79
Paul Cercueilc11737c2015-11-26 14:44:46 +010080static int usb_get_version(const struct iio_context *ctx,
81 unsigned int *major, unsigned int *minor, char git_tag[8])
82{
83 return iiod_client_get_version(ctx->pdata->iiod_client,
84 EP_OPS, major, minor, git_tag);
85}
86
Paul Cercueile06f4ce2015-04-20 12:59:31 +020087static void usb_shutdown(struct iio_context *ctx)
88{
89 iio_mutex_destroy(ctx->pdata->lock);
90
91 iiod_client_destroy(ctx->pdata->iiod_client);
92
93 libusb_close(ctx->pdata->hdl);
94 libusb_exit(ctx->pdata->ctx);
95}
96
97static const struct iio_backend_ops usb_ops = {
Paul Cercueilc11737c2015-11-26 14:44:46 +010098 .get_version = usb_get_version,
Paul Cercueile06f4ce2015-04-20 12:59:31 +020099 .shutdown = usb_shutdown,
100};
101
102static ssize_t write_data_sync(struct iio_context_pdata *pdata,
103 int ep, const char *data, size_t len)
104{
105 int transferred, ret;
106
107 ret = libusb_bulk_transfer(pdata->hdl, ep | LIBUSB_ENDPOINT_OUT,
108 (char *) data, len, &transferred, DEFAULT_TIMEOUT_MS);
109 if (ret)
110 return -libusb_to_errno(ret);
111 else
112 return transferred != len ? -EIO : len;
113}
114
115static ssize_t read_data_sync(struct iio_context_pdata *pdata,
116 int ep, char *buf, size_t len)
117{
118 int transferred, ret;
119
120 ret = libusb_bulk_transfer(pdata->hdl, ep | LIBUSB_ENDPOINT_IN,
121 buf, len, &transferred, DEFAULT_TIMEOUT_MS);
122 if (ret)
123 return -libusb_to_errno(ret);
124 else
125 return transferred;
126}
127
128static struct iiod_client_ops usb_iiod_client_ops = {
129 .write = write_data_sync,
130 .read = read_data_sync,
131 .read_line = read_data_sync,
132};
133
134struct iio_context * usb_create_context(unsigned short vid, unsigned short pid)
135{
136 libusb_context *usb_ctx;
137 libusb_device_handle *hdl;
138 struct iio_context *ctx;
139 struct iio_context_pdata *pdata;
140 unsigned int i;
141 int ret;
142
143 pdata = calloc(1, sizeof(*pdata));
144 if (!pdata) {
145 ERROR("Unable to allocate pdata\n");
146 ret = -ENOMEM;
147 goto err_set_errno;
148 }
149
150 pdata->lock = iio_mutex_create();
151 if (!pdata->lock) {
152 ERROR("Unable to create mutex\n");
153 ret = -ENOMEM;
154 goto err_free_pdata;
155 }
156
157 pdata->iiod_client = iiod_client_new(pdata, pdata->lock,
158 &usb_iiod_client_ops);
159 if (!pdata->iiod_client) {
160 ERROR("Unable to create IIOD client\n");
161 ret = -errno;
162 goto err_destroy_mutex;
163 }
164
165 ret = libusb_init(&usb_ctx);
166 if (ret) {
167 ret = -libusb_to_errno(ret);
168 ERROR("Unable to init libusb: %i\n", ret);
169 goto err_free_pdata;
170 }
171
172 hdl = libusb_open_device_with_vid_pid(usb_ctx, vid, pid);
173 if (!hdl) {
174 ERROR("Unable to find device 0x%04hx:0x%04hx\n", vid, pid);
175 ret = -ENODEV;
176 goto err_libusb_exit;
177 }
178
179 libusb_set_auto_detach_kernel_driver(hdl, true);
180
181 ret = libusb_claim_interface(hdl, 0);
182 if (ret) {
183 ret = -libusb_to_errno(ret);
184 ERROR("Unable to claim interface 0: %i\n", ret);
185 goto err_libusb_close;
186 }
187
188 pdata->ctx = usb_ctx;
189 pdata->hdl = hdl;
190
191 ctx = iiod_client_create_context(pdata->iiod_client, EP_OPS);
192 if (!ctx)
193 goto err_libusb_close;
194
195 ctx->name = "usb";
196 ctx->ops = &usb_ops;
197 ctx->pdata = pdata;
198
199 DEBUG("Initializing context...\n");
200 ret = iio_context_init(ctx);
201 if (ret < 0)
202 goto err_context_destroy;
203
204 return ctx;
205
206err_context_destroy:
207 iio_context_destroy(ctx);
208 errno = -ret;
209 return NULL;
210
211err_libusb_close:
212 libusb_close(hdl);
213err_libusb_exit:
214 libusb_exit(usb_ctx);
215err_destroy_iiod_client:
216 iiod_client_destroy(pdata->iiod_client);
217err_destroy_mutex:
218 iio_mutex_destroy(pdata->lock);
219err_free_pdata:
220 free(pdata);
221err_set_errno:
222 errno = -ret;
223 return NULL;
224}