diff options
Diffstat (limited to 'device.c')
| -rw-r--r-- | device.c | 225 |
1 files changed, 214 insertions, 11 deletions
| @@ -18,39 +18,242 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
| 18 | 18 | ||
| 19 | */ | 19 | */ |
| 20 | 20 | ||
| 21 | #define _BSD_SOURCE | ||
| 22 | |||
| 21 | #ifdef HAVE_CONFIG_H | 23 | #ifdef HAVE_CONFIG_H |
| 22 | #include <config.h> | 24 | #include <config.h> |
| 23 | #endif | 25 | #endif |
| 24 | 26 | ||
| 27 | #include <netinet/in.h> | ||
| 28 | #include <netinet/tcp.h> | ||
| 25 | #include <stdlib.h> | 29 | #include <stdlib.h> |
| 30 | #include <string.h> | ||
| 26 | #include "device.h" | 31 | #include "device.h" |
| 27 | #include "usb.h" | 32 | #include "usb.h" |
| 28 | #include "log.h" | 33 | #include "log.h" |
| 29 | 34 | ||
| 30 | int device_id; | 35 | int next_device_id; |
| 31 | /* | 36 | |
| 32 | int get_next_device_id(void) | 37 | enum mux_protocol { |
| 38 | MUX_PROTO_VERSION = 0, | ||
| 39 | MUX_PROTO_TCP = IPPROTO_TCP, | ||
| 40 | }; | ||
| 41 | |||
| 42 | enum mux_dev_state { | ||
| 43 | MUXDEV_INIT, | ||
| 44 | MUXDEV_ACTIVE, | ||
| 45 | MUXDEV_DEAD | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct mux_header | ||
| 49 | { | ||
| 50 | uint32_t protocol; | ||
| 51 | uint32_t length; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct version_header | ||
| 55 | { | ||
| 56 | uint32_t major; | ||
| 57 | uint32_t minor; | ||
| 58 | uint32_t padding; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct mux_device | ||
| 62 | { | ||
| 63 | struct usb_device *usbdev; | ||
| 64 | int id; | ||
| 65 | enum mux_dev_state state; | ||
| 66 | }; | ||
| 67 | |||
| 68 | static int num_devs; | ||
| 69 | static struct mux_device *device_list; | ||
| 70 | |||
| 71 | static int alloc_device(void) | ||
| 72 | { | ||
| 73 | int i; | ||
| 74 | for(i=0; i<num_devs; i++) { | ||
| 75 | if(!device_list[i].usbdev) | ||
| 76 | return i; | ||
| 77 | } | ||
| 78 | num_devs++; | ||
| 79 | device_list = realloc(device_list, sizeof(*device_list) * num_devs); | ||
| 80 | memset(&device_list[num_devs-1], 0, sizeof(*device_list)); | ||
| 81 | return num_devs - 1; | ||
| 82 | } | ||
| 83 | |||
| 84 | static int get_next_device_id(void) | ||
| 33 | { | 85 | { |
| 34 | int i; | 86 | int i; |
| 35 | while(1) { | 87 | while(1) { |
| 36 | for(i=0; i<num_devs; i++) { | 88 | for(i=0; i<num_devs; i++) { |
| 37 | if(device_list[i].dev && device_list[i].id == device_id) { | 89 | if(device_list[i].usbdev && device_list[i].id == next_device_id) { |
| 38 | device_id++; | 90 | next_device_id++; |
| 39 | break; | 91 | break; |
| 40 | } | 92 | } |
| 41 | } | 93 | } |
| 42 | if(i < num_devs) | 94 | if(i >= num_devs) |
| 95 | return next_device_id++; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, void *data, int length) | ||
| 100 | { | ||
| 101 | unsigned char *buffer; | ||
| 102 | int hdrlen; | ||
| 103 | int res; | ||
| 104 | |||
| 105 | switch(proto) { | ||
| 106 | case MUX_PROTO_VERSION: | ||
| 107 | hdrlen = sizeof(struct version_header); | ||
| 43 | break; | 108 | break; |
| 109 | case MUX_PROTO_TCP: | ||
| 110 | hdrlen = sizeof(struct tcphdr); | ||
| 111 | break; | ||
| 112 | default: | ||
| 113 | usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length); | ||
| 114 | return -1; | ||
| 115 | } | ||
| 116 | usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length); | ||
| 117 | |||
| 118 | int total = sizeof(struct mux_header) + hdrlen + length; | ||
| 119 | |||
| 120 | if(total > USB_MTU) { | ||
| 121 | usbmuxd_log(LL_ERROR, "Tried to send packet larger than USB MTU (hdr %d data %d total %d) to device %d", hdrlen, length, total, dev->id); | ||
| 122 | return -1; | ||
| 44 | } | 123 | } |
| 45 | return device_id++; | 124 | |
| 125 | buffer = malloc(total); | ||
| 126 | struct mux_header *mhdr = (struct mux_header *)buffer; | ||
| 127 | mhdr->protocol = htonl(proto); | ||
| 128 | mhdr->length = htonl(total);; | ||
| 129 | memcpy(buffer + sizeof(struct mux_header), header, hdrlen); | ||
| 130 | if(data && length) | ||
| 131 | memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length); | ||
| 132 | |||
| 133 | if((res = usb_send(dev->usbdev, buffer, total)) < 0) { | ||
| 134 | usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res); | ||
| 135 | free(buffer); | ||
| 136 | return res; | ||
| 137 | } | ||
| 138 | return mhdr->length; | ||
| 46 | } | 139 | } |
| 47 | */ | 140 | |
| 48 | void device_add(struct usb_device *dev) | 141 | static void device_version_input(struct mux_device *dev, struct version_header *vh) |
| 142 | { | ||
| 143 | if(dev->state != MUXDEV_INIT) { | ||
| 144 | usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id); | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | vh->major = ntohl(vh->major); | ||
| 148 | vh->minor = ntohl(vh->minor); | ||
| 149 | if(vh->major != 1 || vh->minor != 0) { | ||
| 150 | usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor); | ||
| 151 | return; | ||
| 152 | } | ||
| 153 | usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", vh->major, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev)); | ||
| 154 | } | ||
| 155 | |||
| 156 | static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, int payload_length) | ||
| 157 | { | ||
| 158 | |||
| 159 | } | ||
| 160 | |||
| 161 | |||
| 162 | void device_data_input(struct usb_device *usbdev, unsigned char *buffer, int length) | ||
| 163 | { | ||
| 164 | int i; | ||
| 165 | struct mux_device *dev; | ||
| 166 | for(i=0; i<num_devs; i++) { | ||
| 167 | if(device_list[i].usbdev == usbdev) { | ||
| 168 | dev = &device_list[i]; | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | if(i >= num_devs) { | ||
| 173 | usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); | ||
| 174 | return; | ||
| 175 | } | ||
| 176 | |||
| 177 | usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length); | ||
| 178 | |||
| 179 | struct mux_header *mhdr = (struct mux_header *)buffer; | ||
| 180 | |||
| 181 | if(ntohl(mhdr->length) != length) { | ||
| 182 | usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length); | ||
| 183 | return; | ||
| 184 | } | ||
| 185 | |||
| 186 | struct tcphdr *th; | ||
| 187 | unsigned char *payload; | ||
| 188 | int payload_length; | ||
| 189 | |||
| 190 | switch(ntohl(mhdr->protocol)) { | ||
| 191 | case MUX_PROTO_VERSION: | ||
| 192 | device_version_input(dev, (struct version_header *)(mhdr+1)); | ||
| 193 | break; | ||
| 194 | case MUX_PROTO_TCP: | ||
| 195 | th = (struct tcphdr *)(mhdr+1); | ||
| 196 | payload = (unsigned char *)(th+1); | ||
| 197 | payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header); | ||
| 198 | device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length); | ||
| 199 | break; | ||
| 200 | default: | ||
| 201 | usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol)); | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | |||
| 205 | } | ||
| 206 | |||
| 207 | int device_add(struct usb_device *dev) | ||
| 49 | { | 208 | { |
| 50 | usbmuxd_log(LL_NOTICE, "Connected to new device on location 0x%x with serial number %s", usb_get_location(dev), usb_get_serial(dev)); | 209 | int res; |
| 210 | int id = get_next_device_id(); | ||
| 211 | int idx = alloc_device(); | ||
| 212 | usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(dev), id); | ||
| 213 | device_list[idx].id = id; | ||
| 214 | device_list[idx].usbdev = dev; | ||
| 215 | device_list[idx].state = MUXDEV_INIT; | ||
| 216 | struct version_header vh; | ||
| 217 | vh.major = htonl(1); | ||
| 218 | vh.minor = htonl(0); | ||
| 219 | vh.padding = 0; | ||
| 220 | if((res = send_packet(&device_list[idx], MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) { | ||
| 221 | usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d\n", id); | ||
| 222 | device_list[idx].usbdev = NULL; | ||
| 223 | device_list[idx].state = MUXDEV_DEAD; | ||
| 224 | return res; | ||
| 225 | } | ||
| 226 | return 0; | ||
| 51 | } | 227 | } |
| 52 | 228 | ||
| 53 | void device_remove(struct usb_device *dev) | 229 | void device_remove(struct usb_device *dev) |
| 54 | { | 230 | { |
| 55 | usbmuxd_log(LL_NOTICE, "Removed device on location 0x%x with serial number %s", usb_get_location(dev), usb_get_serial(dev)); | 231 | int i; |
| 232 | for(i=0; i<num_devs; i++) { | ||
| 233 | if(device_list[i].usbdev == dev) { | ||
| 234 | usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", device_list[i].id, usb_get_location(dev)); | ||
| 235 | device_list[i].usbdev = NULL; | ||
| 236 | return; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", dev, usb_get_location(dev)); | ||
| 240 | } | ||
| 241 | |||
| 242 | void device_init(void) | ||
| 243 | { | ||
| 244 | usbmuxd_log(LL_DEBUG, "device_init"); | ||
| 245 | num_devs = 1; | ||
| 246 | device_list = malloc(sizeof(*device_list) * num_devs); | ||
| 247 | memset(device_list, 0, sizeof(*device_list) * num_devs); | ||
| 248 | next_device_id = 1; | ||
| 249 | } | ||
| 250 | |||
| 251 | void device_shutdown(void) | ||
| 252 | { | ||
| 253 | int i; | ||
| 254 | usbmuxd_log(LL_DEBUG, "device_shutdown"); | ||
| 255 | for(i=0; i<num_devs; i++) | ||
| 256 | device_remove(device_list[i].usbdev); | ||
| 257 | free(device_list); | ||
| 258 | device_list = NULL; | ||
| 56 | } | 259 | } |
