1 //
2 // This file is part of Dire Wolf, an amateur radio packet TNC.
3 //
4 // Copyright (C) 2017,2019 John Langner, WB2OSZ
5 //
6 // Parts of this were adapted from "hamlib" which contains the notice:
7 //
8 // * Copyright (c) 2000-2012 by Stephane Fillod
9 // * Copyright (c) 2011 by Andrew Errington
10 // * CM108 detection code Copyright (c) Thomas Sailer used with permission
11 //
12 // This program is free software: you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation, either version 2 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25 /*------------------------------------------------------------------
26 *
27 * Module: cm108.c
28 *
29 * Purpose: Use the CM108/CM119 (or compatible) GPIO pins for the Push To Talk (PTT) Control.
30 *
31 * Description:
32 *
33 * There is an incresing demand for using the GPIO pins of USB audio devices for PTT.
34 * We have a few commercial products:
35 *
36 * DMK URI http://www.dmkeng.com/URI_Order_Page.htm
37 * RB-USB RIM http://www.repeater-builder.com/products/usb-rim-lite.html
38 * RA-35 http://www.masterscommunications.com/products/radio-adapter/ra35.html
39 *
40 * and homebrew projects which are all very similar.
41 *
42 * http://www.qsl.net/kb9mwr/projects/voip/usbfob-119.pdf
43 * http://rtpdir.weebly.com/uploads/1/6/8/7/1687703/usbfob.pdf
44 * http://www.repeater-builder.com/projects/fob/USB-Fob-Construction.pdf
45 * https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/
46 *
47 * Usually GPIO 3 is used because it is easier to tack solder a wire to a pin on the end.
48 *
49 * Soundmodem and hamlib paved the way but didn't get too far.
50 * Dire Wolf 1.3 added HAMLIB support (Linux only) which theoretically allows this in a
51 * painful roundabout way. This is documented in the User Guide, section called,
52 * "Hamlib PTT Example 2: Use GPIO of USB audio adapter. (e.g. DMK URI)"
53 *
54 * It's rather involved and the explantion doesn't cover the case of multiple
55 * USB-Audio adapters. It is not as straightforward as you might expect. Here we have
56 * an example of 3 C-Media USB adapters, a SignaLink USB, a keyboard, and a mouse.
57 *
58 *
59 * VID PID Product Sound ADEVICE HID [ptt]
60 * --- --- ------- ----- ------- ---------
61 * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC1D0c plughw:1,0 /dev/hidraw0
62 * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC1D0p plughw:1,0 /dev/hidraw0
63 * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/controlC1 /dev/hidraw0
64 * 08bb 2904 USB Audio CODEC /dev/snd/pcmC2D0c plughw:2,0 /dev/hidraw2
65 * 08bb 2904 USB Audio CODEC /dev/snd/pcmC2D0p plughw:2,0 /dev/hidraw2
66 * 08bb 2904 USB Audio CODEC /dev/snd/controlC2 /dev/hidraw2
67 * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC0D0c plughw:0,0 /dev/hidraw1
68 * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC0D0p plughw:0,0 /dev/hidraw1
69 * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/controlC0 /dev/hidraw1
70 * ** 0d8c 0008 C-Media USB Audio Device /dev/snd/pcmC4D0c plughw:4,0 /dev/hidraw6
71 * ** 0d8c 0008 C-Media USB Audio Device /dev/snd/pcmC4D0p plughw:4,0 /dev/hidraw6
72 * ** 0d8c 0008 C-Media USB Audio Device /dev/snd/controlC4 /dev/hidraw6
73 * 413c 2010 Dell USB Keyboard /dev/hidraw4
74 * 0461 4d15 USB Optical Mouse /dev/hidraw5
75 *
76 *
77 * The USB soundcards (/dev/snd/pcm...) have an associated Human Interface Device (HID)
78 * corresponding to the GPIO pins which are sometimes connected to pushbuttons.
79 * The mapping has no obvious pattern.
80 *
81 * Sound Card 0 HID 1
82 * Sound Card 1 HID 0
83 * Sound Card 2 HID 2
84 * Sound Card 4 HID 6
85 *
86 * That would be a real challenge if you had to figure that all out and configure manually.
87 * Dire Wolf version 1.5 makes this much more flexible and easier to use by supporting multiple
88 * sound devices and automatically determining the corresponding HID for the PTT signal.
89 *
90 *---------------------------------------------------------------*/
91
92 #ifndef USE_CM108
93
94 #ifdef CM108_MAIN
95
96 #include "direwolf.h"
97 #include "textcolor.h"
98
main(void)99 int main (void)
100 {
101 text_color_init (0); // Turn off text color.
102 #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
103 dw_printf ("CM108 PTT support is not available for BSD.\n");
104 #else
105 dw_printf ("CM108 PTT support was disabled in Makefile.linux.\n");
106 dw_printf ("It was excluded because /usr/include/libudev.h was missing.\n");
107 dw_printf ("Install it with \"sudo apt-get install libudev-dev\" or\n");
108 dw_printf ("\"sudo yum install libudev-devel\" then rebuild.\n");
109 #endif
110 return (0);
111 }
112
113 #endif
114
115 #else // USE_CM108 is defined.
116
117 #include "direwolf.h"
118
119 #include <libudev.h>
120 #include <stdio.h>
121 #include <stdlib.h>
122 #include <locale.h>
123 #include <unistd.h>
124 #include <string.h>
125 #include <regex.h>
126
127 #include <sys/types.h>
128 #include <sys/stat.h>
129 #include <sys/ioctl.h> // ioctl, _IOR
130 #include <fcntl.h>
131 #include <errno.h>
132 #include <linux/hidraw.h> // for HIDIOCGRAWINFO
133
134 #include "textcolor.h"
135 #include "cm108.h"
136
137 static int cm108_write (char *name, int iomask, int iodata);
138
139
140 // The CM108, CM109, and CM119 datasheets all say that idProduct can be in the range
141 // of 0008 to 000f programmable by MSEL and MODE pin. How can we tell the difference?
142
143 // CM108B is 0012.
144 // CM119B is 0013.
145 // CM108AH is 0139 programmable by MSEL and MODE pin.
146 // CM119A is 013A programmable by MSEL and MODE pin.
147
148 // To make matters even more confusing, these can be overridden
149 // with an external EEPROM. Some have 8, rather than 4 GPIO.
150
151 #define CMEDIA_VID 0xd8c // Vendor ID
152 #define CMEDIA_PID1_MIN 0x0008 // range for CM108, CM109, CM119 (no following letters)
153 #define CMEDIA_PID1_MAX 0x000f
154
155 #define CMEDIA_PID_CM108AH 0x0139 // CM108AH
156 #define CMEDIA_PID_CM108AH_alt 0x013c // CM108AH? - see issue 210
157 #define CMEDIA_PID_CM108B 0x0012 // CM108B
158 #define CMEDIA_PID_CM119A 0x013a // CM119A
159 #define CMEDIA_PID_CM119B 0x0013 // CM119B
160 #define CMEDIA_PID_HS100 0x013c // HS100
161
162 // The SSS chips seem to be pretty much compatible but they have only two GPIO.
163 // https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/
164 // Data sheet says VID/PID is from an EEPROM but mentions no default.
165
166 #define SSS_VID 0x0c76 // SSS1621, SSS1623
167 #define SSS_PID1 0x1605
168 #define SSS_PID2 0x1607
169 #define SSS_PID3 0x160b
170
171
172 // Device VID PID Number of GPIO
173 // ------ --- --- --------------
174 // CM108 0d8c 0008-000f * 4
175 // CM108AH 0d8c 0139 * 3 Has GPIO 1,3,4 but not 2
176 // CM108B 0d8c 0012 3 Has GPIO 1,3,4 but not 2
177 // CM109 0d8c 0008-000f * 8
178 // CM119 0d8c 0008-000f * 8
179 // CM119A 0d8c 013a * 8
180 // CM119B 0d8c 0013 8
181 // HS100 0d8c 013c 0 (issue 210 reported 013c
182 // being seen for CM108AH)
183 //
184 // SSS1621 0c76 1605 2 per ZL3AME, Can't find data sheet
185 // SSS1623 0c76 1607,160b 2 per ZL3AME, Not in data sheet.
186 //
187 // * idProduct programmable by MSEL and MODE pin.
188 //
189
190 // CMedia pin GPIO Notes
191 // ---------- ---- -----
192 // 43 1
193 // 11 2 N.C. for CM108AH, CM108B
194 // 13 3 Most popular for PTT because it is on the end.
195 // 15 4
196 // 16 5 CM109, CM119, CM119A, CM119B only
197 // 17 6 "
198 // 20 7 "
199 // 22 8 "
200
201 // Test for supported devices.
202
203 #define GOOD_DEVICE(v,p) ( (v == CMEDIA_VID && ((p >= CMEDIA_PID1_MIN && p <= CMEDIA_PID1_MAX) \
204 || p == CMEDIA_PID_CM108AH \
205 || p == CMEDIA_PID_CM108AH_alt \
206 || p == CMEDIA_PID_CM108B \
207 || p == CMEDIA_PID_CM119A \
208 || p == CMEDIA_PID_CM119B )) \
209 || \
210 (v == SSS_VID && (p == SSS_PID1 || p == SSS_PID2 || p == SSS_PID3)) )
211
212 // Look out for null source pointer, and avoid buffer overflow on destination.
213
214 #define SAFE_STRCPY(to,from) { if (from != NULL) { strncpy(to,from,sizeof(to)); to[sizeof(to)-1] = '\0'; } }
215
216
217 // Used to process regular expression matching results.
218
substr_se(char * dest,const char * src,int start,int endp1)219 static void substr_se (char *dest, const char *src, int start, int endp1)
220 {
221 int len = endp1 - start;
222
223 if (start < 0 || endp1 < 0 || len <= 0) {
224 dest[0] = '\0';
225 return;
226 }
227 memcpy (dest, src + start, len);
228 dest[len] = '\0';
229
230 } /* end substr_se */
231
232
233 /*
234 * Result of taking inventory of USB soundcards and USB HIDs.
235 */
236
237 struct thing_s {
238 int vid; // vendor id, displayed as four hexadecimal digits.
239 int pid; // product id, displayed as four hexadecimal digits.
240 char card_number[8]; // Number. e.g. 2 for plughw:2,0
241 char card_name[32]; // Name, assigned by system (e.g. Device_1) or by udev rule.
242 char product[32]; // product name (e.g. manufacturer, model)
243 char devnode_sound[22]; // e.g. /dev/snd/pcmC0D0p
244 char plughw[72]; // Above in more familiar format e.g. plughw:0,0
245 // Oversized to silence a compiler warning.
246 char plughw2[72]; // With name rather than number.
247 char devpath[128]; // Kernel dev path. Does not include /sys mount point.
248 char devnode_hidraw[17]; // e.g. /dev/hidraw3
249 char devnode_usb[25]; // e.g. /dev/bus/usb/001/012
250 // This is what we use to match up audio and HID.
251 };
252
253 int cm108_inventory (struct thing_s *things, int max_things);
254
255
256 /*-------------------------------------------------------------------
257 *
258 * Name: main
259 *
260 * Purpose: Useful utility to list USB audio and HID devices.
261 *
262 *------------------------------------------------------------------*/
263
264 //#define EXTRA 1
265
266 #define MAXX_THINGS 60
267
268 #ifdef CM108_MAIN
269
main(void)270 int main (void)
271 {
272 struct thing_s things[MAXX_THINGS];
273 int num_things;
274 int i;
275
276 text_color_init (0); // Turn off text color.
277
278 // Take inventory of USB Audio adapters and other HID devices.
279
280 num_things = cm108_inventory (things, MAXX_THINGS);
281
282 dw_printf (" VID PID %-*s %-*s %-*s %-*s %-*s"
283 #if EXTRA
284 " %-*s"
285 #endif
286 "\n", (int)sizeof(things[0].product), "Product",
287 (int)sizeof(things[0].devnode_sound), "Sound",
288 (int)sizeof(things[0].plughw)/5, "ADEVICE",
289 (int)sizeof(things[0].plughw2)/4, "ADEVICE",
290 (int)sizeof(things[0].devnode_hidraw), "HID [ptt]"
291 #if EXTRA
292 , (int)sizeof(things[0].devnode_usb), "USB"
293 #endif
294 );
295
296 dw_printf (" --- --- %-*s %-*s %-*s %-*s %-*s"
297 #if EXTRA
298 " %-*s"
299 #endif
300 "\n", (int)sizeof(things[0].product), "-------",
301 (int)sizeof(things[0].devnode_sound), "-----",
302 (int)sizeof(things[0].plughw)/5, "-------",
303 (int)sizeof(things[0].plughw2)/4, "-------",
304 (int)sizeof(things[0].devnode_hidraw), "---------"
305 #if EXTRA
306 , (int)sizeof(things[0].devnode_usb), "---"
307 #endif
308 );
309 for (i = 0; i < num_things; i++) {
310
311 dw_printf ("%2s %04x %04x %-*s %-*s %-*s %-*s %-*s"
312 #if EXTRA
313 " %-*s"
314 #endif
315 "\n",
316 GOOD_DEVICE(things[i].vid,things[i].pid) ? "**" : " ",
317 things[i].vid, things[i].pid,
318 (int)sizeof(things[i].product), things[i].product,
319 (int)sizeof(things[i].devnode_sound), things[i].devnode_sound,
320 (int)sizeof(things[0].plughw)/5, things[i].plughw,
321 (int)sizeof(things[0].plughw2)/4, things[i].plughw2,
322 (int)sizeof(things[i].devnode_hidraw), things[i].devnode_hidraw
323 #if EXTRA
324 , (int)sizeof(things[i].devnode_usb), things[i].devnode_usb
325 #endif
326 );
327 //dw_printf (" %-*s\n", (int)sizeof(things[i].devpath), things[i].devpath);
328 }
329
330 static const char *suggested_names[] = {"Fred", "Wilma", "Pebbles", "Dino", "Barney", "Betty", "Bamm_Bamm" };
331 int iname = 0;
332
333 // From example in https://alsa.opensrc.org/Udev
334
335 dw_printf ("\n");
336 dw_printf ("** = Can use Audio Adapter GPIO for PTT.\n");
337 dw_printf ("\n");
338 dw_printf ("Notice that each USB Audio adapter is assigned a number and a name. These are not predictable so you could\n");
339 dw_printf ("end up using the wrong adapter after adding or removing other USB devices or after rebooting. You can assign a\n");
340 dw_printf ("name to each USB adapter so you can refer to the same one each time. This can be based on any characteristics\n");
341 dw_printf ("that makes them unique such as product id or serial number. Unfortunately these devices don't have unique serial\n");
342 dw_printf ("numbers so how can we tell them apart? A name can also be assigned based on the physical USB socket.\n");
343 dw_printf ("Create a file like \"/etc/udev/rules.d/85-my-usb-audio.rules\" with the following contents and then reboot.\n");
344 dw_printf ("\n");
345 dw_printf ("SUBSYSTEM!=\"sound\", GOTO=\"my_usb_audio_end\"\n");
346 dw_printf ("ACTION!=\"add\", GOTO=\"my_usb_audio_end\"\n");
347
348 // Consider only the 'devnode' paths that end with "card" and a number.
349 // Replace the number with a question mark.
350
351 regex_t devpath_re;
352 char emsg[100];
353 // Drop any "/sys" at the beginning.
354 int e = regcomp (&devpath_re, "(/devices/.+/card)[0-9]$", REG_EXTENDED);
355 if (e) {
356 regerror (e, &devpath_re, emsg, sizeof(emsg));
357 text_color_set(DW_COLOR_ERROR);
358 dw_printf("INTERNAL ERROR: %s:%d: %s\n", __FILE__, __LINE__, emsg);
359 return (-1);
360 }
361
362 for (i = 0; i < num_things; i++) {
363 if (i == 0 || strcmp(things[i].devpath,things[i-1].devpath) != 0) {
364 regmatch_t devpath_match[2];
365 if (regexec (&devpath_re, things[i].devpath, 2, devpath_match, 0) == 0) {
366 char without_number[256];
367 substr_se (without_number, things[i].devpath, devpath_match[1].rm_so, devpath_match[1].rm_eo);
368 dw_printf ("DEVPATH==\"%s?\", ATTR{id}=\"%s\"\n", without_number, suggested_names[iname]);
369 if (iname < 6) iname++;
370 }
371 }
372 }
373 dw_printf ("LABEL=\"my_usb_audio_end\"\n");
374 dw_printf ("\n");
375
376 return (0);
377 }
378
379 #endif // CM108_MAIN
380
381
382
383 /*-------------------------------------------------------------------
384 *
385 * Name: cm108_inventory
386 *
387 * Purpose: Take inventory of USB audio and HID.
388 *
389 * Inputs: max_things - Maximum number of items to collect.
390 *
391 * Outputs: things - Array of items collected.
392 * Corresponding sound device and HID are merged into one item.
393 *
394 * Returns: Number of items placed in things array.
395 * Should be in the range of 0 thru max_things.
396 * -1 for a bad unexpected error.
397 *
398 *------------------------------------------------------------------*/
399
400
cm108_inventory(struct thing_s * things,int max_things)401 int cm108_inventory (struct thing_s *things, int max_things)
402 {
403 struct udev *udev;
404 struct udev_enumerate *enumerate;
405 struct udev_list_entry *devices, *dev_list_entry;
406 struct udev_device *dev;
407 struct udev_device *parentdev;
408
409 char const *pattrs_id = NULL;
410 char const *pattrs_number = NULL;
411 char card_devpath[128] = "";
412
413 int num_things = 0;
414 memset (things, 0, sizeof(struct thing_s) * max_things);
415
416 /*
417 * First get a list of the USB audio devices.
418 * This is based on the example in http://www.signal11.us/oss/udev/
419 */
420 udev = udev_new();
421 if (!udev) {
422 text_color_set(DW_COLOR_ERROR);
423 dw_printf("INTERNAL ERROR: Can't create udev.\n");
424 return (-1);
425 }
426
427 enumerate = udev_enumerate_new(udev);
428 udev_enumerate_add_match_subsystem(enumerate, "sound");
429 udev_enumerate_scan_devices(enumerate);
430 devices = udev_enumerate_get_list_entry(enumerate);
431 udev_list_entry_foreach(dev_list_entry, devices) {
432 const char *path = udev_list_entry_get_name(dev_list_entry);
433 dev = udev_device_new_from_syspath(udev, path);
434 char const *devnode = udev_device_get_devnode(dev);
435
436 if (devnode == NULL ) {
437 // I'm not happy with this but couldn't figure out how
438 // to get attributes from one level up from the pcmC?D?? node.
439 strlcpy (card_devpath, path, sizeof(card_devpath));
440 pattrs_id = udev_device_get_sysattr_value(dev,"id");
441 pattrs_number = udev_device_get_sysattr_value(dev,"number");
442 //dw_printf (" >card_devpath = %s\n", card_devpath);
443 //dw_printf (" >>pattrs_id = %s\n", pattrs_id);
444 //dw_printf (" >>pattrs_number = %s\n", pattrs_number);
445 }
446 else {
447 parentdev = udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device");
448 if (parentdev != NULL) {
449 char const *p;
450 int vid = 0;
451 int pid = 0;
452
453 p = udev_device_get_sysattr_value(parentdev,"idVendor");
454 if (p != NULL) vid = strtol(p, NULL, 16);
455 p = udev_device_get_sysattr_value(parentdev,"idProduct");
456 if (p != NULL) pid = strtol(p, NULL, 16);
457
458 if (num_things < max_things) {
459 things[num_things].vid = vid;
460 things[num_things].pid = pid;
461 SAFE_STRCPY (things[num_things].card_name, pattrs_id);
462 SAFE_STRCPY (things[num_things].card_number, pattrs_number);
463 SAFE_STRCPY (things[num_things].product, udev_device_get_sysattr_value(parentdev,"product"));
464 SAFE_STRCPY (things[num_things].devnode_sound, devnode);
465 SAFE_STRCPY (things[num_things].devnode_usb, udev_device_get_devnode(parentdev));
466 strlcpy (things[num_things].devpath, card_devpath, sizeof(things[num_things].devpath));
467 num_things++;
468 }
469 udev_device_unref(parentdev);
470 }
471 }
472 }
473 udev_enumerate_unref(enumerate);
474 udev_unref(udev);
475
476 /*
477 * Now merge in all of the USB HID.
478 */
479 udev = udev_new();
480 if (!udev) {
481 text_color_set(DW_COLOR_ERROR);
482 dw_printf("INTERNAL ERROR: Can't create udev.\n");
483 return (-1);
484 }
485
486 enumerate = udev_enumerate_new(udev);
487 udev_enumerate_add_match_subsystem(enumerate, "hidraw");
488 udev_enumerate_scan_devices(enumerate);
489 devices = udev_enumerate_get_list_entry(enumerate);
490 udev_list_entry_foreach(dev_list_entry, devices) {
491 const char *path = udev_list_entry_get_name(dev_list_entry);
492 dev = udev_device_new_from_syspath(udev, path);
493 char const *devnode = udev_device_get_devnode(dev);
494 if (devnode != NULL) {
495 parentdev = udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device");
496 if (parentdev != NULL) {
497 char const *p;
498 int vid = 0;
499 int pid = 0;
500
501 p = udev_device_get_sysattr_value(parentdev,"idVendor");
502 if (p != NULL) vid = strtol(p, NULL, 16);
503 p = udev_device_get_sysattr_value(parentdev,"idProduct");
504 if (p != NULL) pid = strtol(p, NULL, 16);
505
506 int j, matched = 0;
507 char const *usb = udev_device_get_devnode(parentdev);
508
509 // Add hidraw name to any matching existing.
510 for (j = 0; j < num_things; j++) {
511 if (things[j].vid == vid && things[j].pid == pid && usb != NULL && strcmp(things[j].devnode_usb,usb) == 0) {
512 matched = 1;
513 SAFE_STRCPY (things[j].devnode_hidraw, devnode);
514 }
515 }
516
517 // If it did not match to existing, add new entry.
518 if (matched == 0 && num_things < max_things) {
519 things[num_things].vid = vid;
520 things[num_things].pid = pid;
521 SAFE_STRCPY (things[num_things].product, udev_device_get_sysattr_value(parentdev,"product"));
522 SAFE_STRCPY (things[num_things].devnode_hidraw, devnode);
523 SAFE_STRCPY (things[num_things].devnode_usb, usb);
524 SAFE_STRCPY (things[num_things].devpath, udev_device_get_devpath(dev));
525 num_things++;
526 }
527 udev_device_unref(parentdev);
528 }
529 }
530 }
531 udev_enumerate_unref(enumerate);
532 udev_unref(udev);
533
534 /*
535 * Seeing the form /dev/snd/pcmC4D0p will be confusing to many because we
536 * would generally something like plughw:4,0 for in the direwolf configuration file.
537 * Construct the more familiar form.
538 * Previously we only used the numeric form. In version 1.6, the name is listed as well
539 * and we describe how to assign names based on the physical USB socket for repeatability.
540 */
541 int i;
542 regex_t pcm_re;
543 char emsg[100];
544 int e = regcomp (&pcm_re, "pcmC([0-9]+)D([0-9]+)[cp]", REG_EXTENDED);
545 if (e) {
546 regerror (e, &pcm_re, emsg, sizeof(emsg));
547 text_color_set(DW_COLOR_ERROR);
548 dw_printf("INTERNAL ERROR: %s:%d: %s\n", __FILE__, __LINE__, emsg);
549 return (-1);
550 }
551
552 for (i = 0; i < num_things; i++) {
553 regmatch_t match[3];
554
555 if (regexec (&pcm_re, things[i].devnode_sound, 3, match, 0) == 0) {
556 char c[32], d[32];
557 substr_se (c, things[i].devnode_sound, match[1].rm_so, match[1].rm_eo);
558 substr_se (d, things[i].devnode_sound, match[2].rm_so, match[2].rm_eo);
559 snprintf (things[i].plughw, sizeof(things[i].plughw), "plughw:%s,%s", c, d);
560 snprintf (things[i].plughw2, sizeof(things[i].plughw), "plughw:%s,%s", things[i].card_name, d);
561 }
562 }
563
564 return (num_things);
565
566 } /* end cm108_inventory */
567
568
569 /*-------------------------------------------------------------------
570 *
571 * Name: cm108_find_ptt
572 *
573 * Purpose: Try to find /dev/hidraw corresponding to a USB audio "card."
574 *
575 * Inputs: output_audio_device
576 * - Used in the ADEVICE configuration.
577 * This can take many forms such as:
578 * surround41:CARD=Fred,DEV=0
579 * surround41:Fred,0
580 * surround41:Fred
581 * plughw:2,3
582 * In our case we just need to extract the card number or name.
583 *
584 * ptt_device_size - Size of result area to avoid buffer overflow.
585 *
586 * Outputs: ptt_device - Device name, something like /dev/hidraw2.
587 * Will be emptry string if no match found.
588 *
589 * Returns: none
590 *
591 *------------------------------------------------------------------*/
592
cm108_find_ptt(char * output_audio_device,char * ptt_device,int ptt_device_size)593 void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_device_size)
594 {
595 struct thing_s things[MAXX_THINGS];
596 int num_things;
597 int i;
598
599 //dw_printf ("DEBUG: cm108_find_ptt('%s')\n", output_audio_device);
600
601 strlcpy (ptt_device, "", ptt_device_size);
602 num_things = cm108_inventory (things, MAXX_THINGS);
603
604 regex_t sound_re;
605 char emsg[100];
606 int e = regcomp (&sound_re, ".+:(CARD=)?([A-Za-z0-9_]+)(,.*)?", REG_EXTENDED);
607 if (e) {
608 regerror (e, &sound_re, emsg, sizeof(emsg));
609 text_color_set(DW_COLOR_ERROR);
610 dw_printf("INTERNAL ERROR: %s:%d: %s\n", __FILE__, __LINE__, emsg);
611 return;
612 }
613
614 char num_or_name[64];
615 strcpy (num_or_name, "");
616 regmatch_t sound_match[4];
617 if (regexec (&sound_re, output_audio_device, 4, sound_match, 0) == 0) {
618 substr_se (num_or_name, output_audio_device, sound_match[2].rm_so, sound_match[2].rm_eo);
619 //dw_printf ("DEBUG: Got '%s' from '%s'\n", num_or_name, output_audio_device);
620 }
621 if (strlen(num_or_name) == 0) {
622 text_color_set(DW_COLOR_ERROR);
623 dw_printf ("Could not extract card number or name from %s\n", output_audio_device);
624 dw_printf ("Can't automatically find matching HID for PTT.\n");
625 return;
626 }
627
628 for (i = 0; i < num_things; i++) {
629 //dw_printf ("DEBUG: i=%d, card_name='%s', card_number='%s'\n", i, things[i].card_name, things[i].card_number);
630 if (strcmp(num_or_name,things[i].card_name) == 0 || strcmp(num_or_name,things[i].card_number) == 0) {
631 //dw_printf ("DEBUG: success! returning '%s'\n", things[i].devnode_hidraw);
632 strlcpy (ptt_device, things[i].devnode_hidraw, ptt_device_size);
633 if ( ! GOOD_DEVICE(things[i].vid,things[i].pid) ) {
634 text_color_set(DW_COLOR_ERROR);
635 dw_printf ("Warning: USB audio card %s (%s) is not a device known to work with GPIO PTT.\n",
636 things[i].card_number, things[i].card_name);
637 }
638 return;
639 }
640 }
641
642 } /* end cm108_find_ptt */
643
644
645
646 /*-------------------------------------------------------------------
647 *
648 * Name: cm108_set_gpio_pin
649 *
650 * Purpose: Set one GPIO pin of the CM108 or similar.
651 *
652 * Inputs: name - Name of device such as /dev/hidraw2.
653 *
654 * num - GPIO number, range 1 thru 8.
655 *
656 * state - 1 for on, 0 for off.
657 *
658 * Returns: 0 for success. -1 for error.
659 *
660 * Errors: A descriptive error message will be printed for any problem.
661 *
662 * Future: For our initial implementation we are making the simplifying
663 * restriction of using only one GPIO pin per device and limit
664 * configuratin to PTT only.
665 * Longer term, we might want to have DCD, and maybe other
666 * controls thru the same chip.
667 * In this case, we would need to retain bit masks for each
668 * device so new data can be merged with old before sending it out.
669 *
670 *------------------------------------------------------------------*/
671
672 #if TESTCM
673
674 // Switch pin between input, output-low, and output-high.
675
676 // gcc -DTESTCM=1 -DUSE_CM108 cm108.c textcolor.c misc.a -ludev
677
main(int argc,char * argv[])678 int main (int argc, char *argv[])
679 {
680 #define MODE_IN 0
681 #define MODE_OUT 0x04 // GPIO 3 = bit 2
682 #define OUT_LOW 0
683 #define OUT_HIGH 0x04
684
685 if (argc != 2) {
686 text_color_set(DW_COLOR_ERROR);
687 dw_printf ("Specify HID path on command line.\n");
688 exit (1);
689 }
690
691 while (1) {
692 text_color_set(DW_COLOR_INFO);
693 dw_printf ("Input-L\n");
694 cm108_write (argv[1], MODE_IN, OUT_LOW);
695 sleep(5);
696 dw_printf ("Input-H\n");
697 cm108_write (argv[1], MODE_IN, OUT_HIGH);
698 sleep(5);
699 dw_printf ("Out-LOW\n");
700 cm108_write (argv[1], MODE_OUT, OUT_LOW);
701 sleep(5);
702 dw_printf ("out-HIGH\n");
703 cm108_write (argv[1], MODE_OUT, OUT_HIGH);
704 sleep(5);
705 }
706 }
707
708 #endif
709
710
cm108_set_gpio_pin(char * name,int num,int state)711 int cm108_set_gpio_pin (char *name, int num, int state)
712 {
713 int iomask;
714 int iodata;
715
716 if (num < 1 || num > 8) {
717 text_color_set(DW_COLOR_ERROR);
718 dw_printf("%s CM108 GPIO number %d must be in range of 1 thru 8.\n", name, num);
719 return (-1);
720 }
721
722 if (state != 0 && state != 1) {
723 text_color_set(DW_COLOR_ERROR);
724 dw_printf("%s CM108 GPIO state %d must be 0 or 1.\n", name, state);
725 return (-1);
726 }
727
728 iomask = 1 << (num - 1); // 0=input, 1=output
729 iodata = state << (num - 1); // 0=low, 1=high
730
731 return (cm108_write (name, iomask, iodata));
732
733 } /* end cm108_set_gpio_pin */
734
735
736 /*-------------------------------------------------------------------
737 *
738 * Name: cm108_write
739 *
740 * Purpose: Set the GPIO pins of the CM108 or similar.
741 *
742 * Inputs: name - Name of device such as /dev/hidraw2.
743 *
744 * iomask - Bit mask for I/O direction.
745 * LSB is GPIO1, bit 1 is GPIO2, etc.
746 * 1 for output, 0 for input.
747 *
748 * iodata - Output data, same bit order as iomask.
749 *
750 * Returns: 0 for success. -1 for error.
751 *
752 * Errors: A descriptive error message will be printed for any problem.
753 *
754 * Description: This is the lowest level function.
755 * An application probably wants to use cm108_set_gpio_pin.
756 *
757 *------------------------------------------------------------------*/
758
cm108_write(char * name,int iomask,int iodata)759 static int cm108_write (char *name, int iomask, int iodata)
760 {
761 int fd;
762 struct hidraw_devinfo info;
763 char io[5];
764 int n;
765
766 //text_color_set(DW_COLOR_DEBUG);
767 //dw_printf ("TEMP DEBUG cm108_write: %s %d %d\n", name, iomask, iodata);
768
769 /*
770 * By default, the USB HID are accessible only by root:
771 *
772 * crw------- 1 root root 249, 1 ... /dev/hidraw1
773 *
774 * How should we handle this?
775 * Manually changing it will revert back on the next reboot or
776 * when the device is removed and reinserted.
777 *
778 * According to various articles on the Internet, we should be able to
779 * add a file to /etc/udev/rules.d. "99-direwolf-cmedia.rules" would be a
780 * suitable name. The leading number is the order. We want this to be
781 * near the end. I think the file extension must be ".rules."
782 *
783 * We could completely open it up to everyone like this:
784 *
785 * # Allow ordinary user to access CMedia GPIO for PTT.
786 * SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", MODE="0666"
787 *
788 * Whenever we have CMedia USB audio adapter, it should be accessible by everyone.
789 * This would not apply to other /dev/hidraw* corresponding to keyboard, mouse, etc.
790 *
791 * Notice the == (double =) for testing and := for setting a property.
792 *
793 * If you are concerned about security, you could restrict access to
794 * a particular group, something like this:
795 *
796 * SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", GROUP="audio", MODE="0660"
797 *
798 * I figure "audio" makes more sense than "gpio" because we need to be part of
799 * audio group to use the USB Audio adapter for sound.
800 */
801
802 fd = open (name, O_WRONLY);
803 if (fd == -1) {
804 text_color_set(DW_COLOR_ERROR);
805 dw_printf ("Could not open %s for write, errno=%d\n", name, errno);
806 if (errno == EACCES) { // 13
807 dw_printf ("Type \"ls -l %s\" and verify that it has audio group rw similar to this:\n", name);
808 dw_printf (" crw-rw---- 1 root audio 247, 0 Oct 6 19:24 %s\n", name);
809 dw_printf ("rather than root-only access like this:\n");
810 dw_printf (" crw------- 1 root root 247, 0 Sep 24 09:40 %s\n", name);
811 }
812 return (-1);
813 }
814
815 // Just for fun, let's get the device information.
816
817 #if 1
818 n = ioctl(fd, HIDIOCGRAWINFO, &info);
819 if (n == 0) {
820 if ( ! GOOD_DEVICE(info.vendor, info.product)) {
821 text_color_set(DW_COLOR_ERROR);
822 dw_printf ("ioctl HIDIOCGRAWINFO failed for %s. errno = %d.\n", name, errno);
823 }
824 }
825 else {
826 text_color_set(DW_COLOR_ERROR);
827 dw_printf ("%s is not a supported device type. Proceed at your own risk. vid=%04x pid=%04x\n", name, info.vendor, info.product);
828 }
829 #endif
830 // To make a long story short, I think we need 0 for the first two bytes.
831
832 io[0] = 0;
833 io[1] = 0;
834 // Issue 210 - These were reversed. Fixed in 1.6.
835 io[2] = iodata;
836 io[3] = iomask;
837 io[4] = 0;
838
839 // Writing 4 bytes fails with errno 32, EPIPE, "broken pipe."
840 // Hamlib writes 5 bytes which I don't understand.
841 // Writing 5 bytes works.
842 // I have no idea why. From the CMedia datasheet it looks like we need 4.
843
844 n = write (fd, io, sizeof(io));
845 if (n != sizeof(io)) {
846 // Errors observed during development.
847 // as pi EACCES 13 /* Permission denied */
848 // as root EPIPE 32 /* Broken pipe - Happens if we send 4 bytes */
849
850 text_color_set(DW_COLOR_ERROR);
851 dw_printf ("Write to %s failed, n=%d, errno=%d\n", name, n, errno);
852
853 if (errno == EACCES) {
854 dw_printf ("Type \"ls -l %s\" and verify that it has audio group rw similar to this:\n", name);
855 dw_printf (" crw-rw---- 1 root audio 247, 0 Oct 6 19:24 %s\n", name);
856 dw_printf ("rather than root-only access like this:\n");
857 dw_printf (" crw------- 1 root root 247, 0 Sep 24 09:40 %s\n", name);
858 }
859
860 close (fd);
861 return (-1);
862 }
863
864 close (fd);
865 return (0);
866
867 } /* end cm108_write */
868
869 #endif // ifdef USE_CM108
870
871 /* end cm108.c */
872
873
874