xref: /qemu/backends/tpm/tpm_util.c (revision 89be9e99c83095af25ce16907143c31f2959188b)
156a3c24fSStefan Berger /*
256a3c24fSStefan Berger  * TPM utility functions
356a3c24fSStefan Berger  *
456a3c24fSStefan Berger  *  Copyright (c) 2010 - 2015 IBM Corporation
556a3c24fSStefan Berger  *  Authors:
656a3c24fSStefan Berger  *    Stefan Berger <stefanb@us.ibm.com>
756a3c24fSStefan Berger  *
856a3c24fSStefan Berger  * This library is free software; you can redistribute it and/or
956a3c24fSStefan Berger  * modify it under the terms of the GNU Lesser General Public
1056a3c24fSStefan Berger  * License as published by the Free Software Foundation; either
1156a3c24fSStefan Berger  * version 2 of the License, or (at your option) any later version.
1256a3c24fSStefan Berger  *
1356a3c24fSStefan Berger  * This library is distributed in the hope that it will be useful,
1456a3c24fSStefan Berger  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1556a3c24fSStefan Berger  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1656a3c24fSStefan Berger  * Lesser General Public License for more details.
1756a3c24fSStefan Berger  *
1856a3c24fSStefan Berger  * You should have received a copy of the GNU Lesser General Public
1956a3c24fSStefan Berger  * License along with this library; if not, see <http://www.gnu.org/licenses/>
2056a3c24fSStefan Berger  */
2156a3c24fSStefan Berger 
220430891cSPeter Maydell #include "qemu/osdep.h"
23abc5cda0SStefan Berger #include "qemu/error-report.h"
24*89be9e99SCornelia Huck #include "qapi/error.h"
25*89be9e99SCornelia Huck #include "qapi/visitor.h"
2656a3c24fSStefan Berger #include "tpm_util.h"
2756a3c24fSStefan Berger #include "tpm_int.h"
285086bf97SMarc-André Lureau #include "exec/memory.h"
29*89be9e99SCornelia Huck #include "sysemu/tpm_backend.h"
30*89be9e99SCornelia Huck #include "hw/qdev.h"
3156a3c24fSStefan Berger 
32abc5cda0SStefan Berger #define DEBUG_TPM 0
33abc5cda0SStefan Berger 
34abc5cda0SStefan Berger #define DPRINTF(fmt, ...) do { \
35abc5cda0SStefan Berger     if (DEBUG_TPM) { \
36abc5cda0SStefan Berger         fprintf(stderr, "tpm-util:"fmt"\n", ## __VA_ARGS__); \
37abc5cda0SStefan Berger     } \
38abc5cda0SStefan Berger } while (0)
39abc5cda0SStefan Berger 
40*89be9e99SCornelia Huck /* tpm backend property */
41*89be9e99SCornelia Huck 
42*89be9e99SCornelia Huck static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
43*89be9e99SCornelia Huck                     Error **errp)
44*89be9e99SCornelia Huck {
45*89be9e99SCornelia Huck     DeviceState *dev = DEVICE(obj);
46*89be9e99SCornelia Huck     TPMBackend **be = qdev_get_prop_ptr(dev, opaque);
47*89be9e99SCornelia Huck     char *p;
48*89be9e99SCornelia Huck 
49*89be9e99SCornelia Huck     p = g_strdup(*be ? (*be)->id : "");
50*89be9e99SCornelia Huck     visit_type_str(v, name, &p, errp);
51*89be9e99SCornelia Huck     g_free(p);
52*89be9e99SCornelia Huck }
53*89be9e99SCornelia Huck 
54*89be9e99SCornelia Huck static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
55*89be9e99SCornelia Huck                     Error **errp)
56*89be9e99SCornelia Huck {
57*89be9e99SCornelia Huck     DeviceState *dev = DEVICE(obj);
58*89be9e99SCornelia Huck     Error *local_err = NULL;
59*89be9e99SCornelia Huck     Property *prop = opaque;
60*89be9e99SCornelia Huck     TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop);
61*89be9e99SCornelia Huck     char *str;
62*89be9e99SCornelia Huck 
63*89be9e99SCornelia Huck     if (dev->realized) {
64*89be9e99SCornelia Huck         qdev_prop_set_after_realize(dev, name, errp);
65*89be9e99SCornelia Huck         return;
66*89be9e99SCornelia Huck     }
67*89be9e99SCornelia Huck 
68*89be9e99SCornelia Huck     visit_type_str(v, name, &str, &local_err);
69*89be9e99SCornelia Huck     if (local_err) {
70*89be9e99SCornelia Huck         error_propagate(errp, local_err);
71*89be9e99SCornelia Huck         return;
72*89be9e99SCornelia Huck     }
73*89be9e99SCornelia Huck 
74*89be9e99SCornelia Huck     s = qemu_find_tpm_be(str);
75*89be9e99SCornelia Huck     if (s == NULL) {
76*89be9e99SCornelia Huck         error_setg(errp, "Property '%s.%s' can't find value '%s'",
77*89be9e99SCornelia Huck                    object_get_typename(obj), prop->name, str);
78*89be9e99SCornelia Huck     } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) {
79*89be9e99SCornelia Huck         *be = s; /* weak reference, avoid cyclic ref */
80*89be9e99SCornelia Huck     }
81*89be9e99SCornelia Huck     g_free(str);
82*89be9e99SCornelia Huck }
83*89be9e99SCornelia Huck 
84*89be9e99SCornelia Huck static void release_tpm(Object *obj, const char *name, void *opaque)
85*89be9e99SCornelia Huck {
86*89be9e99SCornelia Huck     DeviceState *dev = DEVICE(obj);
87*89be9e99SCornelia Huck     Property *prop = opaque;
88*89be9e99SCornelia Huck     TPMBackend **be = qdev_get_prop_ptr(dev, prop);
89*89be9e99SCornelia Huck 
90*89be9e99SCornelia Huck     if (*be) {
91*89be9e99SCornelia Huck         tpm_backend_reset(*be);
92*89be9e99SCornelia Huck     }
93*89be9e99SCornelia Huck }
94*89be9e99SCornelia Huck 
95*89be9e99SCornelia Huck const PropertyInfo qdev_prop_tpm = {
96*89be9e99SCornelia Huck     .name  = "str",
97*89be9e99SCornelia Huck     .description = "ID of a tpm to use as a backend",
98*89be9e99SCornelia Huck     .get   = get_tpm,
99*89be9e99SCornelia Huck     .set   = set_tpm,
100*89be9e99SCornelia Huck     .release = release_tpm,
101*89be9e99SCornelia Huck };
102*89be9e99SCornelia Huck 
10356a3c24fSStefan Berger /*
1044a3d8098SAmarnath Valluri  * Write an error message in the given output buffer.
1054a3d8098SAmarnath Valluri  */
1064a3d8098SAmarnath Valluri void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len)
1074a3d8098SAmarnath Valluri {
1084a3d8098SAmarnath Valluri     if (out_len >= sizeof(struct tpm_resp_hdr)) {
1094a3d8098SAmarnath Valluri         struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
1104a3d8098SAmarnath Valluri 
1114a3d8098SAmarnath Valluri         resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
1124a3d8098SAmarnath Valluri         resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
1134a3d8098SAmarnath Valluri         resp->errcode = cpu_to_be32(TPM_FAIL);
1144a3d8098SAmarnath Valluri     }
1154a3d8098SAmarnath Valluri }
1164a3d8098SAmarnath Valluri 
1174a3d8098SAmarnath Valluri bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
1184a3d8098SAmarnath Valluri {
1194a3d8098SAmarnath Valluri     struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
1204a3d8098SAmarnath Valluri 
1214a3d8098SAmarnath Valluri     if (in_len >= sizeof(*hdr)) {
1224a3d8098SAmarnath Valluri         return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
1234a3d8098SAmarnath Valluri     }
1244a3d8098SAmarnath Valluri 
1254a3d8098SAmarnath Valluri     return false;
1264a3d8098SAmarnath Valluri }
1274a3d8098SAmarnath Valluri 
1284a3d8098SAmarnath Valluri /*
12956388eeeSStefan Berger  * Send request to a TPM device. We expect a response within one second.
13056a3c24fSStefan Berger  */
13156388eeeSStefan Berger static int tpm_util_request(int fd,
13256a3c24fSStefan Berger                             unsigned char *request,
13356a3c24fSStefan Berger                             size_t requestlen,
13456388eeeSStefan Berger                             unsigned char *response,
13556388eeeSStefan Berger                             size_t responselen)
13656a3c24fSStefan Berger {
13756a3c24fSStefan Berger     struct tpm_resp_hdr *resp;
13856a3c24fSStefan Berger     fd_set readfds;
13956a3c24fSStefan Berger     int n;
14056a3c24fSStefan Berger     struct timeval tv = {
14156a3c24fSStefan Berger         .tv_sec = 1,
14256a3c24fSStefan Berger         .tv_usec = 0,
14356a3c24fSStefan Berger     };
14456a3c24fSStefan Berger 
14556a3c24fSStefan Berger     n = write(fd, request, requestlen);
14656a3c24fSStefan Berger     if (n < 0) {
14798979cdcSStefan Berger         return -errno;
14856a3c24fSStefan Berger     }
14956a3c24fSStefan Berger     if (n != requestlen) {
15098979cdcSStefan Berger         return -EFAULT;
15156a3c24fSStefan Berger     }
15256a3c24fSStefan Berger 
15356a3c24fSStefan Berger     FD_ZERO(&readfds);
15456a3c24fSStefan Berger     FD_SET(fd, &readfds);
15556a3c24fSStefan Berger 
15656a3c24fSStefan Berger     /* wait for a second */
15756a3c24fSStefan Berger     n = select(fd + 1, &readfds, NULL, NULL, &tv);
15856a3c24fSStefan Berger     if (n != 1) {
15998979cdcSStefan Berger         return -errno;
16056a3c24fSStefan Berger     }
16156a3c24fSStefan Berger 
16256388eeeSStefan Berger     n = read(fd, response, responselen);
16356a3c24fSStefan Berger     if (n < sizeof(struct tpm_resp_hdr)) {
16498979cdcSStefan Berger         return -EFAULT;
16556a3c24fSStefan Berger     }
16656a3c24fSStefan Berger 
16756388eeeSStefan Berger     resp = (struct tpm_resp_hdr *)response;
16856a3c24fSStefan Berger     /* check the header */
16956a3c24fSStefan Berger     if (be32_to_cpu(resp->len) != n) {
17098979cdcSStefan Berger         return -EMSGSIZE;
17156a3c24fSStefan Berger     }
17256a3c24fSStefan Berger 
17356388eeeSStefan Berger     return 0;
17456388eeeSStefan Berger }
17556388eeeSStefan Berger 
17656388eeeSStefan Berger /*
17756388eeeSStefan Berger  * A basic test of a TPM device. We expect a well formatted response header
17856388eeeSStefan Berger  * (error response is fine).
17956388eeeSStefan Berger  */
18056388eeeSStefan Berger static int tpm_util_test(int fd,
18156388eeeSStefan Berger                          unsigned char *request,
18256388eeeSStefan Berger                          size_t requestlen,
18356388eeeSStefan Berger                          uint16_t *return_tag)
18456388eeeSStefan Berger {
18556388eeeSStefan Berger     struct tpm_resp_hdr *resp;
18656388eeeSStefan Berger     unsigned char buf[1024];
18756388eeeSStefan Berger     ssize_t ret;
18856388eeeSStefan Berger 
18956388eeeSStefan Berger     ret = tpm_util_request(fd, request, requestlen,
19056388eeeSStefan Berger                            buf, sizeof(buf));
19156388eeeSStefan Berger     if (ret < 0) {
19256388eeeSStefan Berger         return ret;
19356388eeeSStefan Berger     }
19456388eeeSStefan Berger 
19556388eeeSStefan Berger     resp = (struct tpm_resp_hdr *)buf;
19656a3c24fSStefan Berger     *return_tag = be16_to_cpu(resp->tag);
19756a3c24fSStefan Berger 
19856a3c24fSStefan Berger     return 0;
19956a3c24fSStefan Berger }
20056a3c24fSStefan Berger 
20156a3c24fSStefan Berger /*
20256a3c24fSStefan Berger  * Probe for the TPM device in the back
20356a3c24fSStefan Berger  * Returns 0 on success with the version of the probed TPM set, 1 on failure.
20456a3c24fSStefan Berger  */
20556a3c24fSStefan Berger int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
20656a3c24fSStefan Berger {
20756a3c24fSStefan Berger     /*
20856a3c24fSStefan Berger      * Sending a TPM1.2 command to a TPM2 should return a TPM1.2
20956a3c24fSStefan Berger      * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e)
21056a3c24fSStefan Berger      *
21156a3c24fSStefan Berger      * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the
21256a3c24fSStefan Berger      * header.
21356a3c24fSStefan Berger      * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag
21456a3c24fSStefan Berger      * in the header and an error code.
21556a3c24fSStefan Berger      */
21656a3c24fSStefan Berger     const struct tpm_req_hdr test_req = {
21756a3c24fSStefan Berger         .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
21856a3c24fSStefan Berger         .len = cpu_to_be32(sizeof(test_req)),
21956a3c24fSStefan Berger         .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
22056a3c24fSStefan Berger     };
22156a3c24fSStefan Berger 
22256a3c24fSStefan Berger     const struct tpm_req_hdr test_req_tpm2 = {
22356a3c24fSStefan Berger         .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
22456a3c24fSStefan Berger         .len = cpu_to_be32(sizeof(test_req_tpm2)),
22556a3c24fSStefan Berger         .ordinal = cpu_to_be32(TPM2_CC_ReadClock),
22656a3c24fSStefan Berger     };
22756a3c24fSStefan Berger     uint16_t return_tag;
22856a3c24fSStefan Berger     int ret;
22956a3c24fSStefan Berger 
23056a3c24fSStefan Berger     /* Send TPM 2 command */
23156a3c24fSStefan Berger     ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2,
23256a3c24fSStefan Berger                         sizeof(test_req_tpm2), &return_tag);
23356a3c24fSStefan Berger     /* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
23456a3c24fSStefan Berger     if (!ret && return_tag == TPM2_ST_NO_SESSIONS) {
23556a3c24fSStefan Berger         *tpm_version = TPM_VERSION_2_0;
23656a3c24fSStefan Berger         return 0;
23756a3c24fSStefan Berger     }
23856a3c24fSStefan Berger 
23956a3c24fSStefan Berger     /* Send TPM 1.2 command */
24056a3c24fSStefan Berger     ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req,
24156a3c24fSStefan Berger                         sizeof(test_req), &return_tag);
24256a3c24fSStefan Berger     if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
24356a3c24fSStefan Berger         *tpm_version = TPM_VERSION_1_2;
24456a3c24fSStefan Berger         /* this is a TPM 1.2 */
24556a3c24fSStefan Berger         return 0;
24656a3c24fSStefan Berger     }
24756a3c24fSStefan Berger 
24856a3c24fSStefan Berger     *tpm_version = TPM_VERSION_UNSPEC;
24956a3c24fSStefan Berger 
25056a3c24fSStefan Berger     return 1;
25156a3c24fSStefan Berger }
252abc5cda0SStefan Berger 
253abc5cda0SStefan Berger int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
254abc5cda0SStefan Berger                              size_t *buffersize)
255abc5cda0SStefan Berger {
256abc5cda0SStefan Berger     unsigned char buf[1024];
257abc5cda0SStefan Berger     int ret;
258abc5cda0SStefan Berger 
259abc5cda0SStefan Berger     switch (tpm_version) {
260abc5cda0SStefan Berger     case TPM_VERSION_1_2: {
261abc5cda0SStefan Berger         const struct tpm_req_get_buffer_size {
262abc5cda0SStefan Berger             struct tpm_req_hdr hdr;
263abc5cda0SStefan Berger             uint32_t capability;
264abc5cda0SStefan Berger             uint32_t len;
265abc5cda0SStefan Berger             uint32_t subcap;
266abc5cda0SStefan Berger         } QEMU_PACKED tpm_get_buffer_size = {
267abc5cda0SStefan Berger             .hdr = {
268abc5cda0SStefan Berger                 .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
269abc5cda0SStefan Berger                 .len = cpu_to_be32(sizeof(tpm_get_buffer_size)),
270abc5cda0SStefan Berger                 .ordinal = cpu_to_be32(TPM_ORD_GetCapability),
271abc5cda0SStefan Berger             },
272abc5cda0SStefan Berger             .capability = cpu_to_be32(TPM_CAP_PROPERTY),
273abc5cda0SStefan Berger             .len = cpu_to_be32(sizeof(uint32_t)),
274abc5cda0SStefan Berger             .subcap = cpu_to_be32(TPM_CAP_PROP_INPUT_BUFFER),
275abc5cda0SStefan Berger         };
276abc5cda0SStefan Berger         struct tpm_resp_get_buffer_size {
277abc5cda0SStefan Berger             struct tpm_resp_hdr hdr;
278abc5cda0SStefan Berger             uint32_t len;
279abc5cda0SStefan Berger             uint32_t buffersize;
280abc5cda0SStefan Berger         } QEMU_PACKED *tpm_resp = (struct tpm_resp_get_buffer_size *)buf;
281abc5cda0SStefan Berger 
282abc5cda0SStefan Berger         ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm_get_buffer_size,
283abc5cda0SStefan Berger                                sizeof(tpm_get_buffer_size), buf, sizeof(buf));
284abc5cda0SStefan Berger         if (ret < 0) {
285abc5cda0SStefan Berger             return ret;
286abc5cda0SStefan Berger         }
287abc5cda0SStefan Berger 
288abc5cda0SStefan Berger         if (be32_to_cpu(tpm_resp->hdr.len) != sizeof(*tpm_resp) ||
289abc5cda0SStefan Berger             be32_to_cpu(tpm_resp->len) != sizeof(uint32_t)) {
290abc5cda0SStefan Berger             DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
291abc5cda0SStefan Berger                     be32_to_cpu(tpm_resp->hdr.len), sizeof(*tpm_resp));
292abc5cda0SStefan Berger             DPRINTF("tpm_resp->len = %u, expected = %zu\n",
293abc5cda0SStefan Berger                     be32_to_cpu(tpm_resp->len), sizeof(uint32_t));
294abc5cda0SStefan Berger             error_report("tpm_util: Got unexpected response to "
295abc5cda0SStefan Berger                          "TPM_GetCapability; errcode: 0x%x",
296abc5cda0SStefan Berger                          be32_to_cpu(tpm_resp->hdr.errcode));
297abc5cda0SStefan Berger             return -EFAULT;
298abc5cda0SStefan Berger         }
299abc5cda0SStefan Berger         *buffersize = be32_to_cpu(tpm_resp->buffersize);
300abc5cda0SStefan Berger         break;
301abc5cda0SStefan Berger     }
302abc5cda0SStefan Berger     case TPM_VERSION_2_0: {
303abc5cda0SStefan Berger         const struct tpm2_req_get_buffer_size {
304abc5cda0SStefan Berger             struct tpm_req_hdr hdr;
305abc5cda0SStefan Berger             uint32_t capability;
306abc5cda0SStefan Berger             uint32_t property;
307abc5cda0SStefan Berger             uint32_t count;
308abc5cda0SStefan Berger         } QEMU_PACKED tpm2_get_buffer_size = {
309abc5cda0SStefan Berger             .hdr = {
310abc5cda0SStefan Berger                 .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
311abc5cda0SStefan Berger                 .len = cpu_to_be32(sizeof(tpm2_get_buffer_size)),
312abc5cda0SStefan Berger                 .ordinal = cpu_to_be32(TPM2_CC_GetCapability),
313abc5cda0SStefan Berger             },
314abc5cda0SStefan Berger             .capability = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES),
315abc5cda0SStefan Berger             .property = cpu_to_be32(TPM2_PT_MAX_COMMAND_SIZE),
316abc5cda0SStefan Berger             .count = cpu_to_be32(2), /* also get TPM2_PT_MAX_RESPONSE_SIZE */
317abc5cda0SStefan Berger         };
318abc5cda0SStefan Berger         struct tpm2_resp_get_buffer_size {
319abc5cda0SStefan Berger             struct tpm_resp_hdr hdr;
320abc5cda0SStefan Berger             uint8_t more;
321abc5cda0SStefan Berger             uint32_t capability;
322abc5cda0SStefan Berger             uint32_t count;
323abc5cda0SStefan Berger             uint32_t property1;
324abc5cda0SStefan Berger             uint32_t value1;
325abc5cda0SStefan Berger             uint32_t property2;
326abc5cda0SStefan Berger             uint32_t value2;
327abc5cda0SStefan Berger         } QEMU_PACKED *tpm2_resp = (struct tpm2_resp_get_buffer_size *)buf;
328abc5cda0SStefan Berger 
329abc5cda0SStefan Berger         ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm2_get_buffer_size,
330abc5cda0SStefan Berger                                sizeof(tpm2_get_buffer_size), buf, sizeof(buf));
331abc5cda0SStefan Berger         if (ret < 0) {
332abc5cda0SStefan Berger             return ret;
333abc5cda0SStefan Berger         }
334abc5cda0SStefan Berger 
335abc5cda0SStefan Berger         if (be32_to_cpu(tpm2_resp->hdr.len) != sizeof(*tpm2_resp) ||
336abc5cda0SStefan Berger             be32_to_cpu(tpm2_resp->count) != 2) {
337abc5cda0SStefan Berger             DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n",
338abc5cda0SStefan Berger                     be32_to_cpu(tpm2_resp->hdr.len), sizeof(*tpm2_resp));
339abc5cda0SStefan Berger             DPRINTF("tpm2_resp->len = %u, expected = %u\n",
340abc5cda0SStefan Berger                     be32_to_cpu(tpm2_resp->count), 2);
341abc5cda0SStefan Berger             error_report("tpm_util: Got unexpected response to "
342abc5cda0SStefan Berger                          "TPM2_GetCapability; errcode: 0x%x",
343abc5cda0SStefan Berger                          be32_to_cpu(tpm2_resp->hdr.errcode));
344abc5cda0SStefan Berger             return -EFAULT;
345abc5cda0SStefan Berger         }
346abc5cda0SStefan Berger         *buffersize = MAX(be32_to_cpu(tpm2_resp->value1),
347abc5cda0SStefan Berger                           be32_to_cpu(tpm2_resp->value2));
348abc5cda0SStefan Berger         break;
349abc5cda0SStefan Berger     }
350abc5cda0SStefan Berger     case TPM_VERSION_UNSPEC:
351abc5cda0SStefan Berger         return -EFAULT;
352abc5cda0SStefan Berger     }
353abc5cda0SStefan Berger 
354abc5cda0SStefan Berger     DPRINTF("buffersize of device: %zu\n", *buffersize);
355abc5cda0SStefan Berger 
356abc5cda0SStefan Berger     return 0;
357abc5cda0SStefan Berger }
358