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