add radio_state set/query support
[project/umbim.git] / mbim-dev.c
1 /*
2  * umbim
3  * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <linux/usb/cdc-wdm.h>
16 #include <sys/ioctl.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdint.h>
25
26 #include <libubox/uloop.h>
27
28 #include "mbim.h"
29
30 size_t mbim_bufsize = 0;
31 uint8_t *mbim_buffer = NULL;
32 static struct uloop_fd mbim_fd;
33 static uint32_t expected;
34 int no_close;
35
36 static void mbim_msg_tout_cb(struct uloop_timeout *t)
37 {
38         fprintf(stderr, "ERROR: mbim message timeout\n");
39         mbim_end();
40 }
41
42 static struct uloop_timeout tout = {
43         .cb = mbim_msg_tout_cb,
44 };
45
46 int
47 mbim_send(void)
48 {
49         struct mbim_message_header *hdr = (struct mbim_message_header *) mbim_buffer;
50         int ret = 0;
51
52         if (le32toh(hdr->length) > mbim_bufsize) {
53                 fprintf(stderr, "message too big %d\n", le32toh(hdr->length));
54                 return -1;
55         }
56
57         if (verbose) {
58                 fprintf(stderr, "sending (%d): ", le32toh(hdr->length));
59                 for (ret = 0; ret < le32toh(hdr->length); ret++)
60                         printf("%02x ", ((uint8_t *) mbim_buffer)[ret]);
61                 printf("\n");
62                 printf("  header_type: %04X\n", le32toh(hdr->type));
63                 printf("  header_length: %04X\n", le32toh(hdr->length));
64                 printf("  header_transaction: %04X\n", le32toh(hdr->transaction_id));
65         }
66
67         ret = write(mbim_fd.fd, mbim_buffer, le32toh(hdr->length));
68         if (!ret) {
69                 perror("writing data failed: ");
70         } else {
71                 expected = le32toh(hdr->type) | 0x80000000;
72                 uloop_timeout_set(&tout, 15000);
73         }
74         return ret;
75 }
76
77 static void
78 mbim_recv(struct uloop_fd *u, unsigned int events)
79 {
80         ssize_t cnt = read(u->fd, mbim_buffer, mbim_bufsize);
81         struct mbim_message_header *hdr = (struct mbim_message_header *) mbim_buffer;
82         struct command_done_message *msg = (struct command_done_message *) (hdr + 1);
83         int i;
84
85         if (cnt < 0)
86                 return;
87
88         if (cnt < sizeof(struct mbim_message_header)) {
89                 perror("failed to read() data: ");
90                 return;
91         }
92         if (verbose) {
93                 printf("reading (%zu): ", cnt);
94                 for (i = 0; i < cnt; i++)
95                         printf("%02x ", mbim_buffer[i]);
96                 printf("\n");
97                 printf("  header_type: %04X\n", le32toh(hdr->type));
98                 printf("  header_length: %04X\n", le32toh(hdr->length));
99                 printf("  header_transaction: %04X\n", le32toh(hdr->transaction_id));
100         }
101
102         if (le32toh(hdr->type) == expected)
103                 uloop_timeout_cancel(&tout);
104
105         switch(le32toh(hdr->type)) {
106         case MBIM_MESSAGE_TYPE_OPEN_DONE:
107                 if (current_handler->request() < 0)
108                         mbim_send_close_msg();
109                 break;
110         case MBIM_MESSAGE_TYPE_COMMAND_DONE:
111                 if (verbose) {
112                         printf("  command_id: %04X\n", le32toh(msg->command_id));
113                         printf("  status_code: %04X\n", le32toh(msg->status_code));
114                 }
115                 if (msg->status_code && !msg->buffer_length)
116                         return_code = -le32toh(msg->status_code);
117                 else
118                         return_code = current_handler->response(msg->buffer, le32toh(msg->buffer_length));
119                 if (return_code < 0)
120                         no_close = 0;
121                 mbim_send_close_msg();
122                 break;
123         case MBIM_MESSAGE_TYPE_CLOSE_DONE:
124                 mbim_end();
125                 break;
126         case MBIM_MESSAGE_TYPE_FUNCTION_ERROR:
127                 no_close = 0;
128                 mbim_send_close_msg();
129                 return_code = -1;
130                 break;
131         }
132 }
133
134 void
135 mbim_open(const char *path)
136 {
137         __u16 max;
138         int rc;
139
140         mbim_fd.cb = mbim_recv;
141         mbim_fd.fd = open(path, O_RDWR);
142         if (mbim_fd.fd < 1) {
143                 perror("open failed: ");
144                 exit(-1);
145         }
146         rc = ioctl(mbim_fd.fd, IOCTL_WDM_MAX_COMMAND, &max);
147         if (!rc)
148                 mbim_bufsize = max;
149         else
150                 mbim_bufsize = 512;
151         mbim_buffer = malloc(mbim_bufsize);
152         uloop_fd_add(&mbim_fd, ULOOP_READ);
153 }
154
155 void
156 mbim_end(void)
157 {
158         if (mbim_buffer) {
159                 free(mbim_buffer);
160                 mbim_bufsize = 0;
161                 mbim_buffer = NULL;
162         }
163         uloop_end();
164 }