Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 14 Jan 2019 08:32:15 -0800 (PST)
From:      "Rodney W. Grimes" <freebsd@pdx.rh.CN85.dnsmgr.net>
To:        Lars Engels <lme@freebsd.org>
Cc:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   Re: svn commit: r343020 - head/usr.sbin/bluetooth/bluetooth-config
Message-ID:  <201901141632.x0EGWFud035793@pdx.rh.CN85.dnsmgr.net>
In-Reply-To: <201901140823.x0E8Nmll096242@repo.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
> Author: lme (ports committer)
> Date: Mon Jan 14 08:23:47 2019
> New Revision: 343020
> URL: https://svnweb.freebsd.org/changeset/base/343020
> 
> Log:
>   This new version of bluetooth-config.sh tries to follow the style of other
>   shell scripts in base, removes the bogus "copyleft", adds the BeerWare license
>   header and uses rc.subr(8) new 'enable' keyword for adding entries in
>   rc.conf(5).
>   
>   Submitted by:	erdgeist <erdgeist@erdgeist.org>
>   Approved by:	bapt
>   MFC after:	2 weeks
> 
> Modified:
>   head/usr.sbin/bluetooth/bluetooth-config/bluetooth-config.sh
> 
> Modified: head/usr.sbin/bluetooth/bluetooth-config/bluetooth-config.sh
> ==============================================================================
> --- head/usr.sbin/bluetooth/bluetooth-config/bluetooth-config.sh	Mon Jan 14 07:54:11 2019	(r343019)
> +++ head/usr.sbin/bluetooth/bluetooth-config/bluetooth-config.sh	Mon Jan 14 08:23:47 2019	(r343020)
> @@ -1,9 +1,12 @@
>  #!/bin/sh
>  #-
> -# Copyleft 2019 Dirk Engling
> +# ----------------------------------------------------------------------------
> +# "THE BEER-WARE LICENSE" (Revision 42):
> +# <erdgeist@erdgeist.org> wrote this file. As long as you retain this notice you
> +# can do whatever you want with this stuff. If we meet some day, and you think
> +# this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
> +# ----------------------------------------------------------------------------

Great step forward, but it needs a copyright, without one
the Berne convention applies, and that just makes this a
case of an undocumented copyright.  I didnt think about that
when you showed it with no copyright, there really should be
one in every file to get out of that Berne "undocumented" state.

Thanks,
Rod

>  #
> -# This script is released under the beerware license.
> -#
>  # $FreeBSD$
>  #
>  
> @@ -11,8 +14,6 @@
>  exerr () { echo -e "Error: $*" >&2 ; exit 1; }
>  print_syntax () { echo -e "Syntax: $0 scan [-d device] [-n node]"; exit 1; }
>  
> -# Assuming we are called to do the pair-new-device subcommand first
> -
>  main() {
>  unset node device started bdaddresses retry
>  
> @@ -22,132 +23,140 @@ shift
>  
>  # Get command line options
>  while getopts :d:n: arg; do
> -  case ${arg} in
> -    d) device="$OPTARG";;
> -    n) node="$OPTARG";;
> -    ?) print_syntax;;
> -  esac
> +	case ${arg} in
> +		d) device="$OPTARG";;
> +		n) node="$OPTARG";;
> +		?) print_syntax;;
> +	esac
>  done
>  
>  # No use running without super user rights
> -[ $( id -u ) -eq 0 ] || exerr "$0 must modify files that belong to root.  Re-run as root."
> +if [ $( id -u ) -ne 0 ]; then
> +	exerr "$0 must modify files that belong to root.  Re-run as root."
> +fi
>  
> -known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null | \
> -    /usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1)
> +known_nodes=$( /usr/sbin/hccontrol read_node_list 2>/dev/null |\
> +	/usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1 )
>  
>  # Check if netgraph knows about any HCI nodes
>  if ! [ "${known_nodes}" ]; then
> -  ng_nodes=$(/usr/sbin/ngctl list 2>/dev/null | \
> -    /usr/bin/grep -o "Name: .* Type: ubt" | /usr/bin/cut -d ' ' -f 2)
> +	ng_nodes=$( /usr/sbin/ngctl list 2>/dev/null | \
> +		/usr/bin/grep -o "Name: .* Type: ubt" |/usr/bin/cut -d' ' -f2 )
>  
> -  [ "${ng_nodes}" ] || exerr "No Bluetooth host controllers found."
> +	[ "${ng_nodes}" ] || exerr "No Bluetooth host controllers found."
>  
> -  unset found
> -  for n in ${ng_nodes}; do
> -    if [ "${n}" = "${node%hci}" ]; then
> -      # If we found the node but its stack is not set up, do it now
> -      /usr/sbin/service bluetooth start ${node%hci} || exit 1
> -      found="YES"
> -    fi
> -  done
> +	unset found
> +	for n in ${ng_nodes}; do
> +		if [ "${n}" = "${node%hci}" ]; then
> +			# Found the node but its stack is not set up? Do it now.
> +			/usr/sbin/service bluetooth start ${node%hci} || exit 1
> +			found="YES"
> +		fi
> +	done
>  
> -  # If we have Bluetooth controller nodes without a set up stack,
> -  # ask the user if we shall start it up
> -  if ! [ "${found}" ]; then
> -    printf "No usable Bluetooth host controllers were found.\n"
> -    printf "These host controllers exist in the system:\n  %s" " ${ng_nodes}"
> -    read -p "Choose a host controller to set up: [${ng_nodes%% *}]" node
> -    : ${node:="${ng_nodes%% *}"}
> -    /usr/sbin/service bluetooth start ${node} || exit 1
> -  fi
> +	# If we have Bluetooth controller nodes without a set up stack,
> +	# ask the user if we shall start it up
> +	if ! [ "${found}" ]; then
> +		printf "No usable Bluetooth host controllers were found.\n"
> +		printf "These host controllers exist in the system:\n"
> +		printf "  %s\n" "${ng_nodes}"
> +		prompt="Choose a host controller to set up: [${ng_nodes%% *}]"
> +		read -p "${prompt}" node
> +		: ${node:="${ng_nodes%% *}"}
> +		/usr/sbin/service bluetooth start ${node} || exit 1
> +	fi
>  
> -  # Re-read known nodes
> -  known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null | \
> -    /usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1)
> -  # check if we succeeded in bringing it up
> -  [ "${known_nodes}" ] || exerr "Failed to set up Bluetooth stack"
> +	# Re-read known nodes
> +	known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null |
> +		/usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1 )
> +
> +	# check if we succeeded in bringing it up
> +	[ "${known_nodes}" ] || exerr "Failed to set up Bluetooth stack"
>  fi
>  
>  # if a node was requested on command line, check if it is there
>  if [ "${node}" ]; then
> -  unset found
> -  for n in ${known_nodes}; do
> -    [ "${n}" = "${node}" ] && found="YES"
> -    [ "${n}" = "${node}hci" ] && node="${node}hci" && found="YES"
> -  done
> -  [ "${found}" ] || exerr "Node ${node} not found"
> +	unset found
> +	for n in ${known_nodes}; do
> +		[ "${n}" = "${node}" ] && found="YES"
> +		[ "${n}" = "${node}hci" ] && node="${node}hci" && found="YES"
> +	done
> +	[ "${found}" ] || exerr "Node ${node} not found"
>  fi
>  
>  [ "${node}" ] && node="-n ${node}"
>  
>  while ! [ "${bdaddresses}" ]; do
> -  retry=X${retry}
> -  printf "Scanning for new Bluetooth devices (Attempt %d of 5) ... " ${#retry}
> -  bdaddresses=$( /usr/sbin/hccontrol -N ${node} inquiry 2>/dev/null | \
> -    /usr/bin/grep -o "BD_ADDR: .*" | /usr/bin/cut -d ' ' -f 2 )
> +	retry=X${retry}
> +	printf "Scanning for new Bluetooth devices (Attempt %d of 5) ... " \
> +		${#retry}
> +	bdaddresses=$( /usr/sbin/hccontrol -N ${node} inquiry 2>/dev/null |
> +		/usr/bin/grep -o "BD_ADDR: .*" | /usr/bin/cut -d ' ' -f 2 )
>  
> -  # Count entries and, if a device was requested on command line,
> -  # try to find it
> -  unset found count
> -  for bdaddress in ${bdaddresses}; do
> -    count=X${count}
> -    if [ "${bdaddress}" = "${device}" ]; then
> -      found=YES
> -      bdaddresses="${device}"
> -      count=X
> -      break
> -    fi
> -  done
> +	# Count entries and, if a device was requested on command line,
> +	# try to find it
> +	unset found count
> +	for bdaddress in ${bdaddresses}; do
> +		count=X${count}
> +		if [ "${bdaddress}" = "${device}" ]; then
> +			found=YES
> +			bdaddresses="${device}"
> +			count=X
> +			break
> +		fi
> +	done
>  
> -  # If device was requested on command line but is not found,
> -  # or no devices found at all, rescan until retry is exhausted
> -  if ! [ "${found}" -o "${count}" -a -z "${device}" ]; then
> -    printf "failed.\n"
> -    if [ "${#retry}" -eq 5 ]; then
> -      [ "${device}" ] && exerr "Device ${device} not found"
> -      exerr "No new Bluetooth devices found"
> -    fi
> -    unset bdaddresses
> -    sleep 2
> -    continue
> -  fi
> +	# If device was requested on command line but is not found,
> +	# or no devices found at all, rescan until retry is exhausted
> +	if ! [ "${found}" -o "${count}" -a -z "${device}" ]; then
> +		printf "failed.\n"
> +		if [ "${#retry}" -eq 5 ]; then
> +			[ "${device}" ] && exerr "Device ${device} not found"
> +			exerr "No new Bluetooth devices found"
> +		fi
> +		unset bdaddresses
> +		sleep 2
> +		continue
> +	fi
>  
> -  [ ${#count} -gt 1 ] && plural=s || plural=''
> -  printf "done.\nFound %d new bluetooth device%s (scanning for names):\n" ${#count} ${plural}
> +	[ ${#count} -gt 1 ] && plural=s || plural=''
> +	printf "done.\nFound %d new bluetooth device%s " ${#count} ${plural}
> +	printf "(now scanning for names):\n"
>  
> -  # Looping again for the faster feedback
> -  unset count
> -  for bdaddress in ${bdaddresses}; do
> -    count=X${count}
> -    bdname=$( /usr/bin/bthost -b "${bdaddress}" 2>/dev/null )
> -    friendlyname=$( /usr/sbin/hccontrol Remote_Name_Request ${bdaddress} 2> /dev/null | \
> -      /usr/bin/grep -o "Name: .*" | /usr/bin/cut -d ' ' -f 2- )
> +	# Looping again for the faster feedback
> +	unset count
> +	for bdaddress in ${bdaddresses}; do
> +		count=X${count}
> +		bdname=$( /usr/bin/bthost -b "${bdaddress}" 2>/dev/null )
> +		friendlyname=$( /usr/sbin/hccontrol Remote_Name_Request \
> +			${bdaddress} 2> /dev/null |
> +			/usr/bin/grep -o "Name: .*" |/usr/bin/cut -d ' ' -f 2- )
>  
> -    # sdpcontrol should be able to pull vendor and product id via sdp
> -    printf "[%2d] %s\t\"%s\" (%s)\n" ${#count} "${bdaddress}" "${friendlyname}" "${bdname}"
> +		# sdpcontrol should be able to pull vendor + product id via sdp
> +		printf "[%2d] %s\t\"%s\" (%s)\n" ${#count} "${bdaddress}" \
> +			"${friendlyname}" "${bdname}"
>  
> -    eval bdaddress_${#count}=\${bdaddress}
> -    eval bdname_${#count}=\${bdname}
> -    eval friendlyname_${#count}=\${friendlyname}
> -  done
> +		eval bdaddress_${#count}=\${bdaddress}
> +		eval bdname_${#count}=\${bdname}
> +		eval friendlyname_${#count}=\${friendlyname}
> +	done
>  
> -  # If a device was pre-selected, do not query the user
> -  [ "${device}" ] && topair=1 || unset topair
> +	# If a device was pre-selected, do not query the user
> +	[ "${device}" ] && topair=1 || unset topair
>  
> -  # Even if only one device was found, user may chose 0 to rescan
> -  while ! [ "${topair}" ]; do
> -    if [ ${#count} -eq 1 ]; then
> -      read -p "Select device to pair with [1, or 0 to rescan]: " topair
> -    else
> -      read -p "Select device to pair with [1-${#count}, or 0 to rescan]: " topair
> -    fi
> -    if ! [ "${topair}" -ge 0 -a "${topair}" -le "${#count}" ] 2>/dev/null ; then
> -      printf "Value out of range: %s.\n" {topair}
> -      unset topair
> -    fi
> -  done
> +	# Even if only one device was found, user may chose 0 to rescan
> +	while ! [ "${topair}" ]; do
> +		prompt="Select device to pair with [1"
> +		[ ${#count} -gt 1 ] && prompt="${prompt}-${#count}"
> +		read -p "${prompt}, or 0 to rescan]: " topair
> +		if ! [ "${topair}" -ge 0 -a "${topair}" -le "${#count}" ] \
> +			2>/dev/null ; then
> +			printf "Value out of range: %s.\n" {topair}
> +			unset topair
> +		fi
> +	done
>  
> -  [ "${topair}" -eq "0" ] && unset bdaddresses retry
> +	[ "${topair}" -eq "0" ] && unset bdaddresses retry
>  done
>  
>  eval bdaddress=\${bdaddress_${topair}}
> @@ -156,20 +165,24 @@ eval friendlyname=\${friendlyname_${topair}}
>  
>  # Do we need to add an entry to /etc/bluetooth/hosts?
>  if ! [ "${bdname}" ]; then
> -  printf "\nAdding device ${bdaddress} to /etc/bluetooth/hosts.\n"
> +	printf "\nAdding device ${bdaddress} to /etc/bluetooth/hosts.\n"
>  
> -  while ! [ "${bdname}" ]; do
> -    read -p "Enter friendly name. [${friendlyname}]: " REPLY
> -    : ${REPLY:="${friendlyname}"}
> +	while ! [ "${bdname}" ]; do
> +		read -p "Enter friendly name. [${friendlyname}]: " _r
> +		: ${_r:="${friendlyname}"}
>  
> -    if [ "${REPLY}" ]; then
> -      # Remove white space and non-friendly characters
> -      bdname=$( printf "%s" "${REPLY}" | tr -c '[:alnum:]-,.' _ )
> -      [ "${REPLY}" != "${bdname}" ] && printf "Notice: Using sanitized name \"%s\" in /etc/bluetooth/hosts.\n" "${bdname}"
> -    fi
> -  done
> +		if [ "${_r}" ]; then
> +			# Remove white space and non-friendly characters
> +			bdname=$( printf "%s" "${_r}" | tr -c '[:alnum:]-,.' _ )
> +			if [ "${_r}" != "${bdname}" ]; then
> +				printf "Notice: Using sanitized name"
> +				printf "\"%s\" in /etc/bluetooth/hosts.\n" \
> +					"${bdname}"
> +			fi
> +		fi
> +	done
>  
> -  printf "%s\t%s\n" "${bdaddress}" "${bdname}" >> /etc/bluetooth/hosts
> +	printf "%s\t%s\n" "${bdaddress}" "${bdname}" >> /etc/bluetooth/hosts
>  fi
>  
>  # If scanning for the name did not succeed, resort to bdname
> @@ -183,86 +196,109 @@ fi
>  # Also we cannot really modify the PIN in an existing entry. So we
>  # need to prompt the user to manually do it and restart this script
>  if ! /usr/sbin/service hcsecd enabled; then
> -  printf "\nWarning: hcsecd is not enabled.\nThis daemon manages pairing requests.\n"
> -  read -p "Enable hcsecd? [yes]: " REPLY
> -  case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/sysrc hcsecd_enable="YES";; esac
> +	printf "\nWarning: hcsecd is not enabled.\n"
> +	printf "This daemon manages pairing requests.\n"
> +	read -p "Enable hcsecd? [yes]: " _r
> +	case "${_r}" in
> +		no|n|NO|N|No|nO) ;;
> +		*) /usr/sbin/service hcsecd enable;;
> +	esac
>  fi
> +
>  secd_config=$( /usr/sbin/sysrc -n hcsecd_config )
> -secd_entries=$( /usr/bin/grep -Eo "bdaddr[[:space:]]+(${bdaddress}|${bdname})" ${secd_config} | awk '{ print $2; }' )
> +secd_entries=$( /usr/bin/grep -Eo "bdaddr[[:space:]]+(${bdaddress}|${bdname})" \
> +	${secd_config} | awk '{ print $2; }' )
>  
>  if [ "${secd_entries}" ]; then
> -  printf "\nWarning: An entry for device %s is already present in %s.\n" ${secd_entries} ${secd_config}
> -  printf "To modify pairing information, edit this file and run\n  service hcsecd restart\n"
> -  read -p "Continue? [yes]: " REPLY
> -  case "${REPLY}" in no|n|NO|N|No|nO) exit;; esac
> +	printf "\nWarning: An entry for device %s is already present in %s.\n" \
> +		${secd_entries} ${secd_config}
> +	printf "To modify pairing information, edit this file and run\n"
> +	printf "  service hcsecd restart\n"
> +	read -p "Continue? [yes]: " _r
> +	case "${_r}" in no|n|NO|N|No|nO) exit;; esac
>  else
> -  printf "\nWriting pairing information description block to %s.\n" ${secd_config}
> -  printf "(To get PIN, put device in pairing mode first.)\n"
> -  read -p "Enter PIN [nopin]: " pin
> -  [ "${pin}" ] && pin=\""${pin}"\" || pin="nopin"
> +	printf "\nWriting pairing information description block to %s.\n" \
> +		${secd_config}
> +	printf "(To get PIN, put device in pairing mode first.)\n"
> +	read -p "Enter PIN [nopin]: " pin
> +	[ "${pin}" ] && pin=\""${pin}"\" || pin="nopin"
>  
> -  # Write out new hcsecd config block
> -  printf "\ndevice {\n\tbdaddr\t%s;\n\tname\t\"%s\";\n\tkey\tnokey\;\n\tpin\t%s\;\n}\n" \
> -    "${bdaddress}" "${friendlyname}" "${pin}" >> ${secd_config}
> +	# Write out new hcsecd config block
> +	printf "\ndevice {\n\tbdaddr\t%s;\n\tname\t\"%s\";\n\tkey\tnokey\;\n\tpin\t%s\;\n}\n" \
> +		"${bdaddress}" "${friendlyname}" "${pin}" >> ${secd_config}
>  
> -  # ... and make daemon reload config, TODO: hcsecd should provide a reload hook
> -  /usr/sbin/service hcsecd restart
> +	# ... and make daemon reload config
> +	# TODO: hcsecd should provide a reload hook
> +	/usr/sbin/service hcsecd onerestart
>  
> -  # TODO: we should check if hcsecd succeeded pairing and revert to an old version
> -  # of hcsecd.conf so we can undo adding the block above and retry with a new PIN
> -  # also, if there's a way to force devices to re-pair, try this
> +	# TODO: we should check if hcsecd succeeded pairing and revert to an
> +	# old version of hcsecd.conf so we can undo adding the block above and
> +	# retry with a new PIN
> +	# also, if there's a way to force devices to re-pair, try this
>  fi
>  
>  # now check for specific services to be provided by the device
>  # first up: HID
>  
> -if /usr/sbin/sdpcontrol -a "${bdaddress}" search HID | \
> -   /usr/bin/grep -q "^Record Handle: "; then
> +/usr/sbin/sdpcontrol -a "${bdaddress}" search HID | \
> +	/usr/bin/grep -q "^Record Handle: " || exit 0
>  
> -  printf "\nThis device provides human interface device services.\n"
> -  read -p "Set it up? [yes]: " REPLY
> -  case "${REPLY}" in no|n|NO|N|No|nO) ;;
> -  *)
> -    if ! /usr/sbin/service bthidd enabled; then
> -      printf "\nWarning: bthidd is not enabled."
> -      printf "\nThis daemon manages Bluetooth HID devices.\n"
> -      read -p "Enable bthidd? [yes]: " REPLY
> -      case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/sysrc bthidd_enable="YES";; esac
> -    fi
> +printf "\nThis device provides human interface device services.\n"
> +read -p "Set it up? [yes]: " _r
> +case "${_r}" in
> +	no|n|NO|N|No|nO) exit 0;;
> +	*);;
> +esac
>  
> -    # Check if bthidd already knows about this device
> -    bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
> -      /usr/bin/grep "${bdaddress}" )
> -    if [ "${bthidd_known}" ]; then
> -      printf "Notice: Device %s already known to bthidd.\n" "${bdaddress}"
> -    else
> -      bthidd_config=$( /usr/sbin/sysrc -n bthidd_config )
> -      printf "Writing HID descriptor block to %s ... " "${bthidd_config}"
> -      /usr/sbin/bthidcontrol -a "${bdaddress}" query >> "${bthidd_config}"
> +# Here we have found an HID and were asked to set it up
> +# NOTE: look out for the two exit 0 above if you extend this script
>  
> -      # Re-read config to see if we succeeded adding the device
> -      bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
> -        grep "${bdaddress}" )
> -      if ! [ "${bthidd_known}" ]; then
> -        printf "failed.\n"
> -      else
> -        printf "success.\nTo re-read its config, bthidd must be restarted.\n"
> -        printf "Warning: If a Bluetooth keyboard is being used, the connection might be lost.\n"
> -        printf "It can be manually restarted later with\n  service bthidd restart\n"
> -        read -p "Restart bthidd now? [yes]: " REPLY
> -        case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/service bthidd restart;; esac
> -      fi
> -    fi
> -  ;;
> -  esac
> +if ! /usr/sbin/service bthidd enabled; then
> +	printf "\nWarning: bthidd is not enabled."
> +	printf "\nThis daemon manages Bluetooth HID devices.\n"
> +	read -p "Enable bthidd? [yes]: " _r
> +	case "${_r}" in
> +		no|n|NO|N|No|nO) ;;
> +		 *) /usr/sbin/service bthidd enable;;
> +	esac
>  fi
>  
> +# Check if bthidd already knows about this device
> +bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
> +	/usr/bin/grep "${bdaddress}" )
> +
> +if [ "${bthidd_known}" ]; then
> +	printf "Notice: Device %s already known to bthidd.\n" "${bdaddress}"
> +	return 0
> +fi
> +
> +bthidd_config=$( /usr/sbin/sysrc -n bthidd_config )
> +printf "Writing HID descriptor block to %s ... " "${bthidd_config}"
> +/usr/sbin/bthidcontrol -a "${bdaddress}" query >> "${bthidd_config}"
> +
> +# Re-read config to see if we succeeded adding the device
> +bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
> +	grep "${bdaddress}" )
> +if ! [ "${bthidd_known}" ]; then
> +	printf "failed.\n"
> +else
> +	printf "success.\nTo re-read its config, bthidd must be restarted.\n"
> +	printf "Warning: If a Bluetooth keyboard is being used, the connection"
> +	printf "might be lost.\n"
> +	printf "It can be manually restarted later with\n"
> +	printf " service bthidd restart\n"
> +	read -p "Restart bthidd now? [yes]: " _r
> +	case "${_r}" in
> +		no|n|NO|N|No|nO) ;;
> +		*) /usr/sbin/service bthidd onerestart;;
> +	esac
> +fi
> +
>  }
>  
>  # After function definitions, main() can use them
>  main "$@"
> -
> -exit
> +exit 0
>  
>  # TODO
>  # * If device is a keyboard, offer a text entry test field and if it does
> 
> 

-- 
Rod Grimes                                                 rgrimes@freebsd.org



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201901141632.x0EGWFud035793>