| /* |
| * Copyright 2016 Google Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Derived from hid-example.c, license: |
| * |
| * Hidraw Userspace Example |
| * |
| * Copyright (c) 2010 Alan Ott <alan@signal11.us> |
| * Copyright (c) 2010 Signal 11 Software |
| * |
| * The code may be used by anyone for any purpose, |
| * and can serve as a starting point for developing |
| * applications using hidraw. |
| */ |
| |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/hidraw.h> |
| #include <linux/input.h> |
| #include <linux/types.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <vector> |
| |
| #include "rcu-audio.h" |
| #include "remote_control_audio.pb.h" |
| |
| |
| int main(int argc, char **argv) |
| { |
| int in = -1, out = -1, connected = 0; |
| const char *device; |
| struct sockaddr_in sin; |
| char name[16]; |
| char address[64]; |
| |
| if (argc != 2) { |
| fprintf(stderr, "usage: %s /dev/hidraw#\n", argv[0]); |
| exit(1); |
| } |
| device = argv[1]; |
| |
| if ((in = open(device, O_RDWR)) < 0) { |
| perror("open /dev/hidraw"); |
| exit(1); |
| } |
| |
| memset(&sin, 0, sizeof(sin)); |
| sin.sin_family = AF_INET; |
| sin.sin_port = htons(RCU_AUDIO_PORT); |
| sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| |
| if (ioctl(in, HIDIOCGRAWNAME(sizeof(name)), name) < 0) { |
| perror("HIDIOCGRAWNAME"); |
| exit(1); |
| } |
| if (strcmp(name, "GFRM100") != 0) { |
| fprintf(stderr, "%s is not a GFRM100. Exiting.\n", device); |
| exit(0); |
| } |
| |
| if (ioctl(in, HIDIOCGRAWPHYS(sizeof(address)), address) < 0) { |
| perror("HIDIOCGRAWPHYS"); |
| exit(1); |
| } |
| |
| /* this process will be started out of the hotplug script when a new |
| * remote appears. We either exit if not a GFRM100, or daemonize to |
| * let the hotplug script continue. */ |
| if (daemon(0, 1)) { |
| perror("daemon()"); |
| exit(1); |
| } |
| |
| while (1) { |
| uint8_t data[2048]; |
| ssize_t len = read(in, data, sizeof(data)); |
| |
| if (len < 0) { |
| fprintf(stderr, "GFRM100 has disconnected. Exiting.\n"); |
| exit(0); |
| } |
| |
| if (data[0] != 0xf7) { |
| /* Not an audio packet */ |
| continue; |
| } |
| |
| if (data[1] == 0x01 && len > 4) { |
| rcaudio::AudioSamples samples; |
| std::vector<uint8_t> pkt; |
| |
| samples.set_rc_address(address); |
| samples.set_audio_format(rcaudio::AudioSamples::PCM_16BIT_16KHZ); |
| samples.set_remote_type(rcaudio::AudioSamples::GFRM100); |
| |
| /* |
| * data[0] == 0xf7 |
| * data[1] == 0x01 |
| * data[2] and data[3] are a count of the number of samples. |
| * data[4] == first byte of audio data. |
| */ |
| samples.set_audio_samples(data + 4, len - 4); |
| |
| pkt.resize(samples.ByteSize()); |
| samples.SerializeToArray(&pkt[0], samples.ByteSize()); |
| |
| if (out < 0) { |
| out = get_socket_or_die(); |
| } |
| |
| if (!connected) { |
| if (connect(out, (const struct sockaddr *) &sin, sizeof(sin)) == 0) { |
| connected = 1; |
| } else { |
| sleep(2); /* rate limit how often we retry. */ |
| } |
| } |
| |
| if (connected) { |
| if (send(out, &pkt[0], pkt.size(), 0) != (ssize_t)pkt.size()) { |
| fprintf(stderr, "Audio send failed, will reconnect.\n"); |
| close(out); |
| out = -1; |
| connected = 0; |
| } |
| } |
| } |
| } |
| return 0; |
| } |