Index: head/etc/defaults/rc.conf =================================================================== --- head/etc/defaults/rc.conf (revision 333112) +++ head/etc/defaults/rc.conf (revision 333113) @@ -1,742 +1,743 @@ #!/bin/sh # This is rc.conf - a file full of useful variables that you can set # to change the default startup behavior of your system. You should # not edit this file! Put any overrides into one of the ${rc_conf_files} # instead and you will be able to update these defaults later without # spamming your local configuration information. # # The ${rc_conf_files} files should only contain values which override # values set in this file. This eases the upgrade path when defaults # are changed and new features are added. # # All arguments must be in double or single quotes. # # For a more detailed explanation of all the rc.conf variables, please # refer to the rc.conf(5) manual page. # # $FreeBSD$ ############################################################## ### Important initial Boot-time options #################### ############################################################## # rc_debug can't be set here without interferring with rc.subr's setting it # when the kenv variable rc.debug is set. #rc_debug="NO" # Set to YES to enable debugging output from rc.d rc_info="NO" # Enables display of informational messages at boot. rc_startmsgs="YES" # Show "Starting foo:" messages at boot rcshutdown_timeout="90" # Seconds to wait before terminating rc.shutdown early_late_divider="FILESYSTEMS" # Script that separates early/late # stages of the boot process. Make sure you know # the ramifications if you change this. # See rc.conf(5) for more details. always_force_depends="NO" # Set to check that indicated dependencies are # running during boot (can increase boot time). apm_enable="NO" # Set to YES to enable APM BIOS functions (or NO). apmd_enable="NO" # Run apmd to handle APM event from userland. apmd_flags="" # Flags to apmd (if enabled). ddb_enable="NO" # Set to YES to load ddb scripts at boot. ddb_config="/etc/ddb.conf" # ddb(8) config file. devd_enable="YES" # Run devd, to trigger programs on device tree changes. devd_flags="" # Additional flags for devd(8). devmatch_enable="YES" # Demand load kernel modules based on device ids. #kld_list="" # Kernel modules to load after local disks are mounted kldxref_enable="NO" # Build linker.hints files with kldxref(8). kldxref_clobber="NO" # Overwrite old linker.hints at boot. kldxref_module_path="" # Override kern.module_path. A ';'-delimited list. powerd_enable="NO" # Run powerd to lower our power usage. powerd_flags="" # Flags to powerd (if enabled). tmpmfs="AUTO" # Set to YES to always create an mfs /tmp, NO to never tmpsize="20m" # Size of mfs /tmp if created tmpmfs_flags="-S" # Extra mdmfs options for the mfs /tmp varmfs="AUTO" # Set to YES to always create an mfs /var, NO to never varsize="32m" # Size of mfs /var if created varmfs_flags="-S" # Extra mount options for the mfs /var mfs_type="auto" # "md", "tmpfs", "auto" to prefer tmpfs with md as fallback populate_var="AUTO" # Set to YES to always (re)populate /var, NO to never cleanvar_enable="YES" # Clean the /var directory local_startup="/usr/local/etc/rc.d" # startup script dirs. script_name_sep=" " # Change if your startup scripts' names contain spaces rc_conf_files="/etc/rc.conf /etc/rc.conf.local" # ZFS support zfs_enable="NO" # Set to YES to automatically mount ZFS file systems # ZFSD support zfsd_enable="NO" # Set to YES to automatically start the ZFS fault # management daemon. gptboot_enable="YES" # GPT boot success/failure reporting. # Experimental - test before enabling gbde_autoattach_all="NO" # YES automatically mounts gbde devices from fstab gbde_devices="NO" # Devices to automatically attach (list, or AUTO) gbde_attach_attempts="3" # Number of times to attempt attaching gbde devices gbde_lockdir="/etc" # Where to look for gbde lockfiles # GELI disk encryption configuration. geli_devices="" # List of devices to automatically attach in addition to # GELI devices listed in /etc/fstab. geli_tries="" # Number of times to attempt attaching geli device. # If empty, kern.geom.eli.tries will be used. geli_default_flags="" # Default flags for geli(8). geli_autodetach="YES" # Automatically detach on last close. # Providers are marked as such when all file systems are # mounted. # Example use. #geli_devices="da1 mirror/home" #geli_da1_flags="-p -k /etc/geli/da1.keys" #geli_da1_autodetach="NO" #geli_mirror_home_flags="-k /etc/geli/home.keys" root_rw_mount="YES" # Set to NO to inhibit remounting root read-write. root_hold_delay="30" # Time to wait for root mount hold release. fsck_y_enable="NO" # Set to YES to do fsck -y if the initial preen fails. fsck_y_flags="-T ffs:-R -T ufs:-R" # Additional flags for fsck -y background_fsck="YES" # Attempt to run fsck in the background where possible. background_fsck_delay="60" # Time to wait (seconds) before starting the fsck. growfs_enable="NO" # Set to YES to attempt to grow the root filesystem on boot netfs_types="nfs:NFS smbfs:SMB" # Net filesystems. extra_netfs_types="NO" # List of network extra filesystem types for delayed # mount at startup (or NO). ############################################################## ### Network configuration sub-section ###################### ############################################################## ### Basic network and firewall/security options: ### hostname="" # Set this! hostid_enable="YES" # Set host UUID. hostid_file="/etc/hostid" # File with hostuuid. nisdomainname="NO" # Set to NIS domain if using NIS (or NO). dhclient_program="/sbin/dhclient" # Path to dhcp client program. dhclient_flags="" # Extra flags to pass to dhcp client. #dhclient_flags_fxp0="" # Extra dhclient flags for fxp0 only background_dhclient="NO" # Start dhcp client in the background. #background_dhclient_fxp0="YES" # Start dhcp client on fxp0 in the background. synchronous_dhclient="NO" # Start dhclient directly on configured # interfaces during startup. defaultroute_delay="30" # Time to wait for a default route on a DHCP interface. defaultroute_carrier_delay="5" # Time to wait for carrier while waiting for a default route. netif_enable="YES" # Set to YES to initialize network interfaces netif_ipexpand_max="2048" # Maximum number of IP addrs in a range spec. wpa_supplicant_program="/usr/sbin/wpa_supplicant" wpa_supplicant_flags="-s" # Extra flags to pass to wpa_supplicant wpa_supplicant_conf_file="/etc/wpa_supplicant.conf" # firewall_enable="NO" # Set to YES to enable firewall functionality firewall_script="/etc/rc.firewall" # Which script to run to set up the firewall firewall_type="UNKNOWN" # Firewall type (see /etc/rc.firewall) firewall_quiet="NO" # Set to YES to suppress rule display firewall_logging="NO" # Set to YES to enable events logging firewall_logif="NO" # Set to YES to create logging-pseudo interface firewall_flags="" # Flags passed to ipfw when type is a file firewall_coscripts="" # List of executables/scripts to run after # firewall starts/stops firewall_client_net="192.0.2.0/24" # IPv4 Network address for "client" # firewall. #firewall_client_net_ipv6="2001:db8:2:1::/64" # IPv6 network prefix for # "client" firewall. firewall_simple_iif="ed1" # Inside network interface for "simple" # firewall. firewall_simple_inet="192.0.2.16/28" # Inside network address for "simple" # firewall. firewall_simple_oif="ed0" # Outside network interface for "simple" # firewall. firewall_simple_onet="192.0.2.0/28" # Outside network address for "simple" # firewall. #firewall_simple_iif_ipv6="ed1" # Inside IPv6 network interface for "simple" # firewall. #firewall_simple_inet_ipv6="2001:db8:2:800::/56" # Inside IPv6 network prefix # for "simple" firewall. #firewall_simple_oif_ipv6="ed0" # Outside IPv6 network interface for "simple" # firewall. #firewall_simple_onet_ipv6="2001:db8:2:0::/56" # Outside IPv6 network prefix # for "simple" firewall. firewall_myservices="" # List of TCP ports on which this host # offers services for "workstation" firewall. firewall_allowservices="" # List of IPs which have access to # $firewall_myservices for "workstation" # firewall. firewall_trusted="" # List of IPs which have full access to this # host for "workstation" firewall. firewall_logdeny="NO" # Set to YES to log default denied incoming # packets for "workstation" firewall. firewall_nologports="135-139,445 1026,1027 1433,1434" # List of TCP/UDP ports # for which denied incoming packets are not # logged for "workstation" firewall. firewall_nat_enable="NO" # Enable kernel NAT (if firewall_enable == YES) firewall_nat_interface="" # Public interface or IPaddress to use firewall_nat_flags="" # Additional configuration parameters dummynet_enable="NO" # Load the dummynet(4) module ipfw_netflow_enable="NO" # Enable netflow logging via ng_netflow ip_portrange_first="NO" # Set first dynamically allocated port ip_portrange_last="NO" # Set last dynamically allocated port ike_enable="NO" # Enable IKE daemon (usually racoon or isakmpd) ike_program="/usr/local/sbin/isakmpd" # Path to IKE daemon ike_flags="" # Additional flags for IKE daemon ipsec_enable="NO" # Set to YES to run setkey on ipsec_file ipsec_file="/etc/ipsec.conf" # Name of config file for setkey natd_program="/sbin/natd" # path to natd, if you want a different one. natd_enable="NO" # Enable natd (if firewall_enable == YES). natd_interface="" # Public interface or IPaddress to use. natd_flags="" # Additional flags for natd. ipfilter_enable="NO" # Set to YES to enable ipfilter functionality ipfilter_program="/sbin/ipf" # where the ipfilter program lives ipfilter_rules="/etc/ipf.rules" # rules definition file for ipfilter, see # /usr/src/contrib/ipfilter/rules for examples ipfilter_flags="" # additional flags for ipfilter ipnat_enable="NO" # Set to YES to enable ipnat functionality ipnat_program="/sbin/ipnat" # where the ipnat program lives ipnat_rules="/etc/ipnat.rules" # rules definition file for ipnat ipnat_flags="" # additional flags for ipnat ipmon_enable="NO" # Set to YES for ipmon; needs ipfilter or ipnat ipmon_program="/sbin/ipmon" # where the ipfilter monitor program lives ipmon_flags="-Ds" # typically "-Ds" or "-D /var/log/ipflog" ipfs_enable="NO" # Set to YES to enable saving and restoring # of state tables at shutdown and boot ipfs_program="/sbin/ipfs" # where the ipfs program lives ipfs_flags="" # additional flags for ipfs pf_enable="NO" # Set to YES to enable packet filter (pf) pf_rules="/etc/pf.conf" # rules definition file for pf pf_program="/sbin/pfctl" # where the pfctl program lives pf_flags="" # additional flags for pfctl pflog_enable="NO" # Set to YES to enable packet filter logging pflog_logfile="/var/log/pflog" # where pflogd should store the logfile pflog_program="/sbin/pflogd" # where the pflogd program lives pflog_flags="" # additional flags for pflogd ftpproxy_enable="NO" # Set to YES to enable ftp-proxy(8) for pf ftpproxy_flags="" # additional flags for ftp-proxy(8) pfsync_enable="NO" # Expose pf state to other hosts for syncing pfsync_syncdev="" # Interface for pfsync to work through pfsync_syncpeer="" # IP address of pfsync peer host pfsync_ifconfig="" # Additional options to ifconfig(8) for pfsync tcp_extensions="YES" # Set to NO to turn off RFC1323 extensions. log_in_vain="0" # >=1 to log connects to ports w/o listeners. tcp_keepalive="YES" # Enable stale TCP connection timeout (or NO). tcp_drop_synfin="NO" # Set to YES to drop TCP packets with SYN+FIN # NOTE: this violates the TCP specification icmp_drop_redirect="NO" # Set to YES to ignore ICMP REDIRECT packets icmp_log_redirect="NO" # Set to YES to log ICMP REDIRECT packets network_interfaces="auto" # List of network interfaces (or "auto"). cloned_interfaces="" # List of cloned network interfaces to create. #cloned_interfaces="gif0 gif1 gif2 gif3" # Pre-cloning GENERIC config. #ifconfig_lo0="inet 127.0.0.1" # default loopback device configuration. #ifconfig_lo0_alias0="inet 127.0.0.254 netmask 0xffffffff" # Sample alias entry. #ifconfig_ed0_ipv6="inet6 2001:db8:1::1 prefixlen 64" # Sample IPv6 addr entry #ifconfig_ed0_alias0="inet6 2001:db8:2::1 prefixlen 64" # Sample IPv6 alias #ifconfig_fxp0_name="net0" # Change interface name from fxp0 to net0. #vlans_fxp0="101 vlan0" # vlan(4) interfaces for fxp0 device #create_args_vlan0="vlan 102" # vlan tag for vlan0 device #wlans_ath0="wlan0" # wlan(4) interfaces for ath0 device #wlandebug_wlan0="scan+auth+assoc" # Set debug flags with wlandebug(8) #ipv4_addrs_fxp0="192.168.0.1/24 192.168.1.1-5/28" # example IPv4 address entry. # #autobridge_interfaces="bridge0" # List of bridges to check #autobridge_bridge0="tap* vlan0" # Interface glob to automatically add to the bridge # # If you have any sppp(4) interfaces above, you might also want to set # the following parameters. Refer to spppcontrol(8) for their meaning. sppp_interfaces="" # List of sppp interfaces. #sppp_interfaces="...0" # example: sppp over ... #spppconfig_...0="authproto=chap myauthname=foo myauthsecret='top secret' hisauthname=some-gw hisauthsecret='another secret'" # User ppp configuration. ppp_enable="NO" # Start user-ppp (or NO). ppp_program="/usr/sbin/ppp" # Path to user-ppp program. ppp_mode="auto" # Choice of "auto", "ddial", "direct" or "dedicated". # For details see man page for ppp(8). Default is auto. ppp_nat="YES" # Use PPP's internal network address translation or NO. ppp_profile="papchap" # Which profile to use from /etc/ppp/ppp.conf. ppp_user="root" # Which user to run ppp as # Start multiple instances of ppp at boot time #ppp_profile="profile1 profile2 profile3" # Which profiles to use #ppp_profile1_mode="ddial" # Override ppp mode for profile1 #ppp_profile2_nat="NO" # Override nat mode for profile2 # profile3 uses default ppp_mode and ppp_nat ### Network daemon (miscellaneous) ### hostapd_enable="NO" # Run hostap daemon. syslogd_enable="YES" # Run syslog daemon (or NO). syslogd_program="/usr/sbin/syslogd" # path to syslogd, if you want a different one. syslogd_flags="-s" # Flags to syslogd (if enabled). syslogd_oomprotect="YES" # Don't kill syslogd when swap space is exhausted. altlog_proglist="" # List of chrooted applicatioins in /var inetd_enable="NO" # Run the network daemon dispatcher (YES/NO). inetd_program="/usr/sbin/inetd" # path to inetd, if you want a different one. inetd_flags="-wW -C 60" # Optional flags to inetd iscsid_enable="NO" # iSCSI initiator daemon. iscsictl_enable="NO" # iSCSI initiator autostart. iscsictl_flags="-Aa" # Optional flags to iscsictl. hastd_enable="NO" # Run the HAST daemon (YES/NO). hastd_program="/sbin/hastd" # path to hastd, if you want a different one. hastd_flags="" # Optional flags to hastd. ctld_enable="NO" # CAM Target Layer / iSCSI target daemon. local_unbound_enable="NO" # local caching resolver blacklistd_enable="NO" # Run blacklistd daemon (YES/NO). blacklistd_flags="" # Optional flags for blacklistd(8). resolv_enable="YES" # Enable resolv / resolvconf # # kerberos. Do not run the admin daemons on slave servers # kdc_enable="NO" # Run a kerberos 5 KDC (or NO). kdc_program="/usr/libexec/kdc" # path to kerberos 5 KDC kdc_flags="" # Additional flags to the kerberos 5 KDC kadmind_enable="NO" # Run kadmind (or NO) kadmind_program="/usr/libexec/kadmind" # path to kadmind kpasswdd_enable="NO" # Run kpasswdd (or NO) kpasswdd_program="/usr/libexec/kpasswdd" # path to kpasswdd kfd_enable="NO" # Run kfd (or NO) kfd_program="/usr/libexec/kfd" # path to kerberos 5 kfd daemon kfd_flags="" ipropd_master_enable="NO" # Run Heimdal incremental propagation daemon # (master daemon). ipropd_master_program="/usr/libexec/ipropd-master" ipropd_master_flags="" # Flags to ipropd-master. ipropd_master_keytab="/etc/krb5.keytab" # keytab for ipropd-master. ipropd_master_slaves="" # slave node names used for /var/heimdal/slaves. ipropd_slave_enable="NO" # Run Heimdal incremental propagation daemon # (slave daemon). ipropd_slave_program="/usr/libexec/ipropd-slave" ipropd_slave_flags="" # Flags to ipropd-slave. ipropd_slave_keytab="/etc/krb5.keytab" # keytab for ipropd-slave. ipropd_slave_master="" # master node name. gssd_enable="NO" # Run the gssd daemon (or NO). gssd_program="/usr/sbin/gssd" # Path to gssd. gssd_flags="" # Flags for gssd. rwhod_enable="NO" # Run the rwho daemon (or NO). rwhod_flags="" # Flags for rwhod rarpd_enable="NO" # Run rarpd (or NO). rarpd_flags="-a" # Flags to rarpd. bootparamd_enable="NO" # Run bootparamd (or NO). bootparamd_flags="" # Flags to bootparamd pppoed_enable="NO" # Run the PPP over Ethernet daemon. pppoed_provider="*" # Provider and ppp(8) config file entry. pppoed_flags="-P /var/run/pppoed.pid" # Flags to pppoed (if enabled). pppoed_interface="fxp0" # The interface that pppoed runs on. sshd_enable="NO" # Enable sshd sshd_program="/usr/sbin/sshd" # path to sshd, if you want a different one. sshd_flags="" # Additional flags for sshd. ftpd_enable="NO" # Enable stand-alone ftpd. ftpd_program="/usr/libexec/ftpd" # Path to ftpd, if you want a different one. ftpd_flags="" # Additional flags to stand-alone ftpd. ### Network daemon (NFS): All need rpcbind_enable="YES" ### amd_enable="NO" # Run amd service with $amd_flags (or NO). amd_program="/usr/sbin/amd" # path to amd, if you want a different one. amd_flags="-a /.amd_mnt -l syslog /host /etc/amd.map /net /etc/amd.map" amd_map_program="NO" # Can be set to "ypcat -k amd.master" autofs_enable="NO" # Run autofs daemons. automount_flags="" # Flags to automount(8) (if autofs enabled). automountd_flags="" # Flags to automountd(8) (if autofs enabled). autounmountd_flags="" # Flags to autounmountd(8) (if autofs enabled). nfs_client_enable="NO" # This host is an NFS client (or NO). nfs_access_cache="60" # Client cache timeout in seconds nfs_server_enable="NO" # This host is an NFS server (or NO). nfs_server_flags="-u -t" # Flags to nfsd (if enabled). nfs_server_managegids="NO" # The NFS server maps gids for AUTH_SYS (or NO). mountd_enable="NO" # Run mountd (or NO). mountd_flags="-r -S" # Flags to mountd (if NFS server enabled). weak_mountd_authentication="NO" # Allow non-root mount requests to be served. nfs_reserved_port_only="NO" # Provide NFS only on secure port (or NO). nfs_bufpackets="" # bufspace (in packets) for client rpc_lockd_enable="NO" # Run NFS rpc.lockd needed for client/server. rpc_lockd_flags="" # Flags to rpc.lockd (if enabled). rpc_statd_enable="NO" # Run NFS rpc.statd needed for client/server. rpc_statd_flags="" # Flags to rpc.statd (if enabled). rpcbind_enable="NO" # Run the portmapper service (YES/NO). rpcbind_program="/usr/sbin/rpcbind" # path to rpcbind, if you want a different one. rpcbind_flags="" # Flags to rpcbind (if enabled). rpc_ypupdated_enable="NO" # Run if NIS master and SecureRPC (or NO). keyserv_enable="NO" # Run the SecureRPC keyserver (or NO). keyserv_flags="" # Flags to keyserv (if enabled). nfsv4_server_enable="NO" # Enable support for NFSv4 nfscbd_enable="NO" # NFSv4 client side callback daemon nfscbd_flags="" # Flags for nfscbd nfsuserd_enable="NO" # NFSv4 user/group name mapping daemon nfsuserd_flags="" # Flags for nfsuserd ### Network Time Services options: ### timed_enable="NO" # Run the time daemon (or NO). timed_flags="" # Flags to timed (if enabled). ntpdate_enable="NO" # Run ntpdate to sync time on boot (or NO). ntpdate_program="/usr/sbin/ntpdate" # path to ntpdate, if you want a different one. ntpdate_flags="-b" # Flags to ntpdate (if enabled). ntpdate_config="/etc/ntp.conf" # ntpdate(8) configuration file ntpdate_hosts="" # Whitespace-separated list of ntpdate(8) servers. ntpd_enable="NO" # Run ntpd Network Time Protocol (or NO). ntpd_program="/usr/sbin/ntpd" # path to ntpd, if you want a different one. ntpd_config="/etc/ntp.conf" # ntpd(8) configuration file ntpd_sync_on_start="NO" # Sync time on ntpd startup, even if offset is high ntpd_flags="-p /var/run/ntpd.pid -f /var/db/ntpd.drift" # Flags to ntpd (if enabled). ntp_src_leapfile="/etc/ntp/leap-seconds" # Initial source for ntpd leapfile ntp_db_leapfile="/var/db/ntpd.leap-seconds.list" # Working copy (updated weekly) leapfile ntp_leapfile_sources="https://www.ietf.org/timezones/data/leap-seconds.list" # Source from which to fetch leapfile ntp_leapfile_fetch_opts="-mq" # Options to use for ntp leapfile fetch, # e.g. --no-verify-peer ntp_leapfile_expiry_days=30 # Check for new leapfile 30 days prior to # expiry. ntp_leapfile_fetch_verbose="NO" # Be verbose during NTP leapfile fetch # Network Information Services (NIS) options: All need rpcbind_enable="YES" ### nis_client_enable="NO" # We're an NIS client (or NO). nis_client_flags="" # Flags to ypbind (if enabled). nis_ypset_enable="NO" # Run ypset at boot time (or NO). nis_ypset_flags="" # Flags to ypset (if enabled). nis_server_enable="NO" # We're an NIS server (or NO). nis_server_flags="" # Flags to ypserv (if enabled). nis_ypxfrd_enable="NO" # Run rpc.ypxfrd at boot time (or NO). nis_ypxfrd_flags="" # Flags to rpc.ypxfrd (if enabled). nis_yppasswdd_enable="NO" # Run rpc.yppasswdd at boot time (or NO). nis_yppasswdd_flags="" # Flags to rpc.yppasswdd (if enabled). nis_ypldap_enable="NO" # Run ypldap at boot time (or NO). nis_ypldap_flags="" # Flags to ypldap (if enabled). ### SNMP daemon ### # Be sure to understand the security implications of running SNMP v1/v2 # in your network. bsnmpd_enable="NO" # Run the SNMP daemon (or NO). bsnmpd_flags="" # Flags for bsnmpd. ### Network routing options: ### defaultrouter="NO" # Set to default gateway (or NO). static_arp_pairs="" # Set to static ARP list (or leave empty). static_ndp_pairs="" # Set to static NDP list (or leave empty). static_routes="" # Set to static route list (or leave empty). gateway_enable="NO" # Set to YES if this host will be a gateway. routed_enable="NO" # Set to YES to enable a routing daemon. routed_program="/sbin/routed" # Name of routing daemon to use if enabled. routed_flags="-q" # Flags for routing daemon. arpproxy_all="NO" # replaces obsolete kernel option ARP_PROXYALL. forward_sourceroute="NO" # do source routing (only if gateway_enable is set to "YES") accept_sourceroute="NO" # accept source routed packets to us ### Bluetooth ### hcsecd_enable="NO" # Enable hcsecd(8) (or NO) hcsecd_config="/etc/bluetooth/hcsecd.conf" # hcsecd(8) configuration file sdpd_enable="NO" # Enable sdpd(8) (or NO) sdpd_control="/var/run/sdp" # sdpd(8) control socket sdpd_groupname="nobody" # set spdp(8) user/group to run as after sdpd_username="nobody" # it initializes bthidd_enable="NO" # Enable bthidd(8) (or NO) bthidd_config="/etc/bluetooth/bthidd.conf" # bthidd(8) configuration file bthidd_hids="/var/db/bthidd.hids" # bthidd(8) known HID devices file +bthidd_evdev_support="AUTO" # AUTO depends on EVDEV_SUPPORT kernel option rfcomm_pppd_server_enable="NO" # Enable rfcomm_pppd(8) in server mode (or NO) rfcomm_pppd_server_profile="one two" # Profile to use from /etc/ppp/ppp.conf # #rfcomm_pppd_server_one_bdaddr="" # Override local bdaddr for 'one' rfcomm_pppd_server_one_channel="1" # Override local channel for 'one' #rfcomm_pppd_server_one_register_sp="NO" # Override SP and DUN register #rfcomm_pppd_server_one_register_dun="NO" # for 'one' # #rfcomm_pppd_server_two_bdaddr="" # Override local bdaddr for 'two' rfcomm_pppd_server_two_channel="3" # Override local channel for 'two' #rfcomm_pppd_server_two_register_sp="NO" # Override SP and DUN register #rfcomm_pppd_server_two_register_dun="NO" # for 'two' ubthidhci_enable="NO" # Switch an USB BT controller present on #ubthidhci_busnum="3" # bus 3 and addr 2 from HID mode to HCI mode. #ubthidhci_addr="2" # Check usbconfig list to find the correct # numbers for your system. ### Network link/usability verification options netwait_enable="NO" # Enable rc.d/netwait (or NO) #netwait_ip="" # Wait for ping response from any IP in this list. netwait_timeout="60" # Total number of seconds to perform pings. #netwait_if="" # Wait for active link on each intf in this list. netwait_if_timeout="30" # Total number of seconds to monitor link state. ### Miscellaneous network options: ### icmp_bmcastecho="NO" # respond to broadcast ping packets ### IPv6 options: ### ipv6_network_interfaces="auto" # List of IPv6 network interfaces # (or "auto" or "none"). ipv6_activate_all_interfaces="NO" # If NO, interfaces which have no # corresponding $ifconfig_IF_ipv6 is # marked as IFDISABLED for security # reason. ipv6_defaultrouter="NO" # Set to IPv6 default gateway (or NO). #ipv6_defaultrouter="2002:c058:6301::" # Use this for 6to4 (RFC 3068) ipv6_static_routes="" # Set to static route list (or leave empty). #ipv6_static_routes="xxx" # An example to set fec0:0000:0000:0006::/64 # route toward loopback interface. #ipv6_route_xxx="fec0:0000:0000:0006:: -prefixlen 64 ::1" ipv6_gateway_enable="NO" # Set to YES if this host will be a gateway. ipv6_cpe_wanif="NO" # Set to the upstream interface name if this # node will work as a router to forward IPv6 # packets not explicitly addressed to itself. ipv6_privacy="NO" # Use privacy address on RA-receiving IFs # (RFC 4941) route6d_enable="NO" # Set to YES to enable an IPv6 routing daemon. route6d_program="/usr/sbin/route6d" # Name of IPv6 routing daemon. route6d_flags="" # Flags to IPv6 routing daemon. #route6d_flags="-l" # Example for route6d with only IPv6 site local # addrs. #route6d_flags="-q" # If you want to run a routing daemon on an end # node, you should stop advertisement. #ipv6_network_interfaces="ed0 ep0" # Examples for router # or static configuration for end node. # Choose correct prefix value. #ipv6_prefix_ed0="fec0:0000:0000:0001 fec0:0000:0000:0002" # Examples for rtr. #ipv6_prefix_ep0="fec0:0000:0000:0003 fec0:0000:0000:0004" # Examples for rtr. ipv6_default_interface="NO" # Default output interface for scoped addrs. # This works only with # ipv6_gateway_enable="NO". rtsol_flags="" # Flags to IPv6 router solicitation. rtsold_enable="NO" # Set to YES to enable an IPv6 router # solicitation daemon. rtsold_flags="-a" # Flags to an IPv6 router solicitation # daemon. rtadvd_enable="NO" # Set to YES to enable an IPv6 router # advertisement daemon. If set to YES, # this router becomes a possible candidate # IPv6 default router for local subnets. rtadvd_interfaces="" # Interfaces rtadvd sends RA packets. stf_interface_ipv4addr="" # Local IPv4 addr for 6to4 IPv6 over IPv4 # tunneling interface. Specify this entry # to enable 6to4 interface. stf_interface_ipv4plen="0" # Prefix length for 6to4 IPv4 addr, # to limit peer addr range. Effective value # is 0-31. stf_interface_ipv6_ifid="0:0:0:1" # IPv6 interface id for stf0. # If you like, you can set "AUTO" for this. stf_interface_ipv6_slaid="0000" # IPv6 Site Level Aggregator for stf0 ipv6_ipv4mapping="NO" # Set to "YES" to enable IPv4 mapped IPv6 addr # communication. (like ::ffff:a.b.c.d) ipv6_ipfilter_rules="/etc/ipf6.rules" # rules definition file for ipfilter, # see /usr/src/contrib/ipfilter/rules # for examples ip6addrctl_enable="YES" # Set to YES to enable default address selection ip6addrctl_verbose="NO" # Set to YES to enable verbose configuration messages ip6addrctl_policy="AUTO" # A pre-defined address selection policy # (ipv4_prefer, ipv6_prefer, or AUTO) ############################################################## ### System console options ################################# ############################################################## keyboard="" # keyboard device to use (default /dev/kbd0). keymap="NO" # keymap in /usr/share/{syscons,vt}/keymaps/* (or NO). keyrate="NO" # keyboard rate to: slow, normal, fast (or NO). keybell="NO" # See kbdcontrol(1) for options. Use "off" to disable. keychange="NO" # function keys default values (or NO). cursor="NO" # cursor type {normal|blink|destructive} (or NO). scrnmap="NO" # screen map in /usr/share/syscons/scrnmaps/* (or NO). font8x16="NO" # font 8x16 from /usr/share/{syscons,vt}/fonts/* (or NO). font8x14="NO" # font 8x14 from /usr/share/{syscons,vt}/fonts/* (or NO). font8x8="NO" # font 8x8 from /usr/share/{syscons,vt}/fonts/* (or NO). blanktime="300" # blank time (in seconds) or "NO" to turn it off. saver="NO" # screen saver: Uses /boot/kernel/${saver}_saver.ko moused_nondefault_enable="YES" # Treat non-default mice as enabled unless # specifically overriden in rc.conf(5). moused_enable="NO" # Run the mouse daemon. moused_type="auto" # See man page for rc.conf(5) for available settings. moused_port="/dev/psm0" # Set to your mouse port. moused_flags="" # Any additional flags to moused. mousechar_start="NO" # if 0xd0-0xd3 default range is occupied in your # language code table, specify alternative range # start like mousechar_start=3, see vidcontrol(1) allscreens_flags="" # Set this vidcontrol mode for all virtual screens allscreens_kbdflags="" # Set this kbdcontrol mode for all virtual screens ############################################################## ### Mail Transfer Agent (MTA) options ###################### ############################################################## mta_start_script="/etc/rc.sendmail" # Script to start your chosen MTA, called by /etc/rc. # Settings for /etc/rc.sendmail and /etc/rc.d/sendmail: sendmail_enable="NO" # Run the sendmail inbound daemon (YES/NO). sendmail_pidfile="/var/run/sendmail.pid" # sendmail pid file sendmail_procname="/usr/sbin/sendmail" # sendmail process name sendmail_flags="-L sm-mta -bd -q30m" # Flags to sendmail (as a server) sendmail_cert_create="YES" # Create a server certificate if none (YES/NO) #sendmail_cert_cn="CN" # CN of the generate certificate sendmail_submit_enable="YES" # Start a localhost-only MTA for mail submission sendmail_submit_flags="-L sm-mta -bd -q30m -ODaemonPortOptions=Addr=localhost" # Flags for localhost-only MTA sendmail_outbound_enable="YES" # Dequeue stuck mail (YES/NO). sendmail_outbound_flags="-L sm-queue -q30m" # Flags to sendmail (outbound only) sendmail_msp_queue_enable="YES" # Dequeue stuck clientmqueue mail (YES/NO). sendmail_msp_queue_flags="-L sm-msp-queue -Ac -q30m" # Flags for sendmail_msp_queue daemon. sendmail_rebuild_aliases="NO" # Run newaliases if necessary (YES/NO). ############################################################## ### Miscellaneous administrative options ################### ############################################################## auditd_enable="NO" # Run the audit daemon. auditd_program="/usr/sbin/auditd" # Path to the audit daemon. auditd_flags="" # Which options to pass to the audit daemon. auditdistd_enable="NO" # Run the audit daemon. auditdistd_program="/usr/sbin/auditdistd" # Path to the auditdistd daemon. auditdistd_flags="" # Which options to pass to the auditdistd daemon. cron_enable="YES" # Run the periodic job daemon. cron_program="/usr/sbin/cron" # Which cron executable to run (if enabled). cron_dst="YES" # Handle DST transitions intelligently (YES/NO) cron_flags="" # Which options to pass to the cron daemon. cfumass_enable="NO" # Create default LUN for cfumass(4). cfumass_dir="/var/cfumass" # File to LUN's contents. cfumass_image="/var/tmp/cfumass.img" # LUN's backing file path. lpd_enable="NO" # Run the line printer daemon. lpd_program="/usr/sbin/lpd" # path to lpd, if you want a different one. lpd_flags="" # Flags to lpd (if enabled). nscd_enable="NO" # Run the nsswitch caching daemon. chkprintcap_enable="NO" # Run chkprintcap(8) before running lpd. chkprintcap_flags="-d" # Create missing directories by default. dumpdev="AUTO" # Device to crashdump to (device name, AUTO, or NO). dumpon_flags="" # Options to pass to dumpon(8), followed by dumpdev. dumpdir="/var/crash" # Directory where crash dumps are to be stored savecore_enable="YES" # Extract core from dump devices if any savecore_flags="-m 10" # Used if dumpdev is enabled above, and present. # By default, only the 10 most recent kernel dumps # are saved. crashinfo_enable="YES" # Automatically generate crash dump summary. crashinfo_program="/usr/sbin/crashinfo" # Script to generate crash dump summary. quota_enable="NO" # turn on quotas on startup (or NO). check_quotas="YES" # Check quotas on startup (or NO). quotaon_flags="-a" # Turn quotas on for all file systems (if enabled) quotaoff_flags="-a" # Turn quotas off for all file systems at shutdown quotacheck_flags="-a" # Check all file system quotas (if enabled) accounting_enable="NO" # Turn on process accounting (or NO). ibcs2_enable="NO" # Ibcs2 (SCO) emulation loaded at startup (or NO). ibcs2_loaders="coff" # List of additional Ibcs2 loaders (or NO). firstboot_sentinel="/firstboot" # Scripts with "firstboot" keyword are run if # this file exists. Should be on a R/W filesystem so # the file can be deleted after the boot completes. # Emulation/compatibility services provided by /etc/rc.d/abi sysvipc_enable="NO" # Load System V IPC primitives at startup (or NO). linux_enable="NO" # Linux binary compatibility loaded at startup (or NO). clear_tmp_enable="NO" # Clear /tmp at startup. clear_tmp_X="YES" # Clear and recreate X11-related directories in /tmp ldconfig_insecure="NO" # Set to YES to disable ldconfig security checks ldconfig_paths="/usr/lib/compat /usr/local/lib /usr/local/lib/compat/pkg" # shared library search paths ldconfig32_paths="/usr/lib32 /usr/lib32/compat" # 32-bit compatibility shared library search paths ldconfigsoft_paths="/usr/libsoft /usr/libsoft/compat /usr/local/libsoft" # soft float compatibility shared library search paths # Note: temporarily with extra stuff for transition ldconfig_paths_aout="/usr/lib/compat/aout /usr/local/lib/aout" # a.out shared library search paths ldconfig_local_dirs="/usr/local/libdata/ldconfig" # Local directories with ldconfig configuration files. ldconfig_local32_dirs="/usr/local/libdata/ldconfig32" # Local directories with 32-bit compatibility ldconfig # configuration files. ldconfig_localsoft_dirs="/usr/local/libdata/ldconfigsoft" # Local directories with soft float compatibility ldconfig # configuration files. kern_securelevel_enable="NO" # kernel security level (see security(7)) kern_securelevel="-1" # range: -1..3 ; `-1' is the most insecure # Note that setting securelevel to 0 will result # in the system booting with securelevel set to 1, as # init(8) will raise the level when rc(8) completes. update_motd="YES" # update version info in /etc/motd (or NO) entropy_boot_file="/boot/entropy" # Set to NO to disable very early # (used at early boot time) entropy caching through reboots. entropy_file="/entropy" # Set to NO to disable late (used when going multi-user) # entropy through reboots. # /var/db/entropy-file is preferred if / is not avail. entropy_dir="/var/db/entropy" # Set to NO to disable caching entropy via cron. entropy_save_sz="4096" # Size of the entropy cache files. entropy_save_num="8" # Number of entropy cache files to save. harvest_mask="511" # Entropy device harvests all but the very invasive sources. # (See 'sysctl kern.random.harvest' and random(4)) dmesg_enable="YES" # Save dmesg(8) to /var/run/dmesg.boot watchdogd_enable="NO" # Start the software watchdog daemon watchdogd_flags="" # Flags to watchdogd (if enabled) devfs_rulesets="/etc/defaults/devfs.rules /etc/devfs.rules" # Files containing # devfs(8) rules. devfs_system_ruleset="" # The name (NOT number) of a ruleset to apply to /dev devfs_set_rulesets="" # A list of /mount/dev=ruleset_name settings to # apply (must be mounted already, i.e. fstab(5)) devfs_load_rulesets="YES" # Enable to always load the default rulesets performance_cx_lowest="NONE" # Online CPU idle state performance_cpu_freq="NONE" # Online CPU frequency economy_cx_lowest="Cmax" # Offline CPU idle state economy_cpu_freq="NONE" # Offline CPU frequency virecover_enable="YES" # Perform housekeeping for the vi(1) editor ugidfw_enable="NO" # Load mac_bsdextended(4) rules on boot bsdextended_script="/etc/rc.bsdextended" # Default mac_bsdextended(4) # ruleset file. newsyslog_enable="YES" # Run newsyslog at startup. newsyslog_flags="-CN" # Newsyslog flags to create marked files mixer_enable="YES" # Run the sound mixer. opensm_enable="NO" # Opensm(8) for infiniband devices defaults to off # rctl(8) requires kernel options RACCT and RCTL rctl_enable="YES" # Load rctl(8) rules on boot rctl_rules="/etc/rctl.conf" # rctl(8) ruleset. See rctl.conf(5). iovctl_files="" # Config files for iovctl(8) ############################################################## ### Jail Configuration (see rc.conf(5) manual page) ########## ############################################################## jail_enable="NO" # Set to NO to disable starting of any jails jail_confwarn="YES" # Prevent warning about obsolete per-jail configuration jail_parallel_start="NO" # Start jails in the background jail_list="" # Space separated list of names of jails jail_reverse_stop="NO" # Stop jails in reverse order ############################################################## ### Define source_rc_confs, the mechanism used by /etc/rc.* ## ### scripts to source rc_conf_files overrides safely. ## ############################################################## if [ -z "${source_rc_confs_defined}" ]; then source_rc_confs_defined=yes source_rc_confs() { local i sourced_files for i in ${rc_conf_files}; do case ${sourced_files} in *:$i:*) ;; *) sourced_files="${sourced_files}:$i:" if [ -r $i ]; then . $i fi ;; esac done # Re-do process to pick up [possibly] redefined $rc_conf_files for i in ${rc_conf_files}; do case ${sourced_files} in *:$i:*) ;; *) sourced_files="${sourced_files}:$i:" if [ -r $i ]; then . $i fi ;; esac done } fi # Allow vendors to override FreeBSD defaults in /etc/default/rc.conf # without the need to carefully manage /etc/rc.conf. if [ -r /etc/defaults/vendor.conf ]; then . /etc/defaults/vendor.conf fi Index: head/etc/rc.d/bthidd =================================================================== --- head/etc/rc.d/bthidd (revision 333112) +++ head/etc/rc.d/bthidd (revision 333113) @@ -1,34 +1,54 @@ #!/bin/sh # # $FreeBSD$ # # PROVIDE: bthidd # REQUIRE: DAEMON hcsecd # BEFORE: LOGIN # KEYWORD: nojail shutdown . /etc/rc.subr name="bthidd" desc="Bluetooth HID daemon" rcvar="bthidd_enable" command="/usr/sbin/${name}" pidfile="/var/run/${name}.pid" start_precmd="bthidd_prestart" +evdev_enabled() +{ + case ${bthidd_evdev_support} in + [Aa][Uu][Tt][Oo]) + check_kern_features evdev_support + return $? + ;; + *) + checkyesno bthidd_evdev_support + return $? + ;; + esac +} + bthidd_prestart() { + if evdev_enabled; then + load_kld -m uinput uinput + fi load_kld -m kbdmux kbdmux load_kld -m vkbd vkbd load_kld -m ng_btsocket ng_btsocket return 0 } load_rc_config $name config="${bthidd_config:-/etc/bluetooth/${name}.conf}" hids="${bthidd_hids:-/var/db/${name}.hids}" command_args="-c ${config} -H ${hids} -p ${pidfile}" +if evdev_enabled; then + command_args="$command_args -u" +fi required_files="${config}" run_rc_command "$1" Index: head/usr.sbin/bluetooth/bthidd/Makefile =================================================================== --- head/usr.sbin/bluetooth/bthidd/Makefile (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/Makefile (revision 333113) @@ -1,16 +1,16 @@ # $Id: Makefile,v 1.6 2006/09/07 21:36:55 max Exp $ # $FreeBSD$ PROG= bthidd MAN= bthidd.8 # bthidd.conf.5 -SRCS= bthidd.c client.c hid.c kbd.c lexer.l parser.y server.c \ - session.c +SRCS= bthidd.c btuinput.c client.c hid.c kbd.c lexer.l parser.y \ + server.c session.c CFLAGS+= -I${.CURDIR} LIBADD+= bluetooth usbhid NO_WMISSING_VARIABLE_DECLARATIONS= .include Index: head/usr.sbin/bluetooth/bthidd/bthid_config.h =================================================================== --- head/usr.sbin/bluetooth/bthidd/bthid_config.h (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/bthid_config.h (revision 333113) @@ -1,76 +1,80 @@ /* * bthid_config.h */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bthid_config.h,v 1.4 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #ifndef _BTHID_CONFIG_H_ #define _BTHID_CONFIG_H_ 1 #define BTHIDD_CONFFILE "/etc/bluetooth/bthidd.conf" #define BTHIDD_HIDSFILE "/var/db/bthidd.hids" struct hid_device { bdaddr_t bdaddr; /* HID device BDADDR */ char * name; /* HID device name */ uint16_t control_psm; /* control PSM */ uint16_t interrupt_psm; /* interrupt PSM */ uint16_t vendor_id; /* primary vendor id */ uint16_t product_id; uint16_t version; unsigned new_device : 1; unsigned reconnect_initiate : 1; unsigned battery_power : 1; unsigned normally_connectable : 1; unsigned keyboard : 1; - unsigned reserved : 11; + unsigned mouse : 1; + unsigned has_wheel : 1; + unsigned has_hwheel : 1; + unsigned has_cons : 1; + unsigned reserved : 7; report_desc_t desc; /* HID report descriptor */ LIST_ENTRY(hid_device) next; /* link to the next */ }; typedef struct hid_device hid_device_t; typedef struct hid_device * hid_device_p; extern char const *config_file; extern char const *hids_file; int32_t read_config_file (void); void clean_config (void); hid_device_p get_hid_device (bdaddr_p bdaddr); hid_device_p get_next_hid_device (hid_device_p d); void print_hid_device (hid_device_p hid_device, FILE *f); int32_t read_hids_file (void); int32_t write_hids_file (void); #endif /* ndef _BTHID_CONFIG_H_ */ Index: head/usr.sbin/bluetooth/bthidd/bthidd.8 =================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.8 (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/bthidd.8 (revision 333113) @@ -1,127 +1,133 @@ .\" Copyright (c) 2006 Maksim Yevmenkin .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $Id: bthidd.8,v 1.1 2006/09/07 21:36:55 max Exp $ .\" $FreeBSD$ .\" -.Dd September 7, 2006 +.Dd April 30, 2018 .Dt BTHIDD 8 .Os .Sh NAME .Nm bthidd .Nd Bluetooth HID daemon .Sh SYNOPSIS .Nm .Fl h .Nm .Op Fl a Ar BD_ADDR .Op Fl c Ar file .Op Fl H Ar file .Op Fl p Ar file .Op Fl t Ar val +.Op Fl u .Sh DESCRIPTION The .Nm daemon handles remote Bluetooth HID devices. .Pp The options are as follows: .Bl -tag -width indent .It Fl a Ar BD_ADDR Specify the local address to listen on. By default, the server will listen on .Dv ANY address. The address can be specified as BD_ADDR or name. If a name was specified, the .Nm daemon will attempt to resolve the name via .Xr bt_gethostbyname 3 . .It Fl c Ar file Specify path to the configuration file. The default path is .Pa /etc/bluetooth/bthidd.conf . .It Fl d Do not detach from the controlling terminal, i.e., run in foreground. .It Fl H Ar file Specify path to the known HIDs file. The default path is .Pa /var/db/bthidd.hids . .It Fl h Display usage message and exit. .It Fl p Ar file Specify path to the PID file. The default path is .Pa /var/run/bthidd.pid . .It Fl t Ar val Specify client rescan interval in seconds. The .Nm daemon will periodically scan for newly configured Bluetooth HID devices or disconnected .Dq passive Bluetooth HID devices and will attempt to establish an outgoing connection. The default rescan interval is 10 seconds. +.It Fl u +Enable support for input event device protocol. +Requires evdev and uinput drivers to be loaded with +.Xr kldload 8 +or compiled into the kernel. .El .Sh KNOWN LIMITATIONS The .Nm daemon currently does not handle key auto repeat and double click mouse events. Those events work under .Xr X 7 just fine, but not in text console. .Pp This manual page needs more work. A manual page documenting the format of the .Pa /etc/bluetooth/bthidd.conf configuration file is needed as well. .Sh FILES .Bl -tag -width ".Pa /etc/bluetooth/bthidd.conf" -compact .It Pa /etc/bluetooth/bthidd.conf .It Pa /var/db/bthidd.hids .It Pa /var/run/bthidd.pid .El .Sh SEE ALSO .Xr kbdmux 4 , .Xr vkbd 4 , .Xr bthidcontrol 8 .Sh AUTHORS .An Maksim Yevmenkin Aq Mt m_evmenkin@yahoo.com .Sh CAVEATS Any Bluetooth HID device that has .Dv HUP_KEYBOARD or .Dv HUP_CONSUMER entries in its descriptor is considered as .Dq keyboard . For each .Dq keyboard Bluetooth HID device, the .Nm daemon will use a separate instance of the virtual keyboard interface .Xr vkbd 4 . Therefore the .Xr kbdmux 4 driver must be used to properly multiplex input from multiple keyboards. Index: head/usr.sbin/bluetooth/bthidd/bthidd.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.c (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/bthidd.c (revision 333113) @@ -1,269 +1,277 @@ /* * bthidd.c */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bthidd.c,v 1.8 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" static int32_t write_pid_file (char const *file); static int32_t remove_pid_file (char const *file); static int32_t elapsed (int32_t tval); static void sighandler (int32_t s); static void usage (void); /* * bthidd */ static int32_t done = 0; /* are we done? */ int32_t main(int32_t argc, char *argv[]) { struct bthid_server srv; struct sigaction sa; char const *pid_file = BTHIDD_PIDFILE; char *ep; - int32_t opt, detach, tval; + int32_t opt, detach, tval, uinput; memset(&srv, 0, sizeof(srv)); memset(&srv.bdaddr, 0, sizeof(srv.bdaddr)); detach = 1; tval = 10; /* sec */ + uinput = 0; - while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) { + while ((opt = getopt(argc, argv, "a:c:dH:hp:t:u")) != -1) { switch (opt) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &srv.bdaddr)) { struct hostent *he; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&srv.bdaddr, he->h_addr, sizeof(srv.bdaddr)); } break; case 'c': /* config file */ config_file = optarg; break; case 'd': /* do not detach */ detach = 0; break; case 'H': /* hids file */ hids_file = optarg; break; case 'p': /* pid file */ pid_file = optarg; break; case 't': /* rescan interval */ tval = strtol(optarg, (char **) &ep, 10); if (*ep != '\0' || tval <= 0) usage(); break; + case 'u': /* enable evdev support */ + uinput = 1; + break; + case 'h': default: usage(); /* NOT REACHED */ } } openlog(BTHIDD_IDENT, LOG_PID|LOG_PERROR|LOG_NDELAY, LOG_USER); /* Become daemon if required */ if (detach && daemon(0, 0) < 0) { syslog(LOG_CRIT, "Could not become daemon. %s (%d)", strerror(errno), errno); exit(1); } /* Install signal handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0 || sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGINT, &sa, NULL) < 0) { syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) < 0) { syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) { syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } if (read_config_file() < 0 || read_hids_file() < 0 || server_init(&srv) < 0 || write_pid_file(pid_file) < 0) exit(1); + srv.uinput = uinput; + for (done = 0; !done; ) { if (elapsed(tval)) client_rescan(&srv); if (server_do(&srv) < 0) break; } server_shutdown(&srv); remove_pid_file(pid_file); clean_config(); closelog(); return (0); } /* * Write pid file */ static int32_t write_pid_file(char const *file) { FILE *pid; assert(file != NULL); if ((pid = fopen(file, "w")) == NULL) { syslog(LOG_ERR, "Could not open file %s. %s (%d)", file, strerror(errno), errno); return (-1); } fprintf(pid, "%d", getpid()); fclose(pid); return (0); } /* * Remote pid file */ static int32_t remove_pid_file(char const *file) { assert(file != NULL); if (unlink(file) < 0) { syslog(LOG_ERR, "Could not unlink file %s. %s (%d)", file, strerror(errno), errno); return (-1); } return (0); } /* * Returns true if desired time interval has elapsed */ static int32_t elapsed(int32_t tval) { static struct timeval last = { 0, 0 }; struct timeval now; gettimeofday(&now, NULL); if (now.tv_sec - last.tv_sec >= tval) { last = now; return (1); } return (0); } /* * Signal handler */ static void sighandler(int32_t s) { syslog(LOG_NOTICE, "Got signal %d, total number of signals %d", s, ++ done); } /* * Display usage and exit */ static void usage(void) { fprintf(stderr, "Usage: %s [options]\n" \ "Where options are:\n" \ " -a address specify address to listen on (default ANY)\n" \ " -c file specify config file name\n" \ " -d run in foreground\n" \ " -H file specify known HIDs file name\n" \ " -h display this message\n" \ " -p file specify PID file name\n" \ " -t tval specify client rescan interval (sec)\n" \ +" -u enable evdev protocol support\n" \ "", BTHIDD_IDENT); exit(255); } Index: head/usr.sbin/bluetooth/bthidd/bthidd.h =================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.h (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/bthidd.h (revision 333113) @@ -1,97 +1,103 @@ /* * bthidd.h */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bthidd.h,v 1.7 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #ifndef _BTHIDD_H_ #define _BTHIDD_H_ 1 #define BTHIDD_IDENT "bthidd" #define BTHIDD_PIDFILE "/var/run/" BTHIDD_IDENT ".pid" struct bthid_session; struct bthid_server { bdaddr_t bdaddr; /* local bdaddr */ int32_t cons; /* /dev/consolectl */ int32_t ctrl; /* control channel (listen) */ int32_t intr; /* intr. channel (listen) */ int32_t maxfd; /* max fd in sets */ + int32_t uinput; /* enable evdev support */ fd_set rfdset; /* read descriptor set */ fd_set wfdset; /* write descriptor set */ LIST_HEAD(, bthid_session) sessions; }; typedef struct bthid_server bthid_server_t; typedef struct bthid_server * bthid_server_p; struct bthid_session { bthid_server_p srv; /* pointer back to server */ int32_t ctrl; /* control channel */ int32_t intr; /* interrupt channel */ int32_t vkbd; /* virual keyboard */ void *ctx; /* product specific dev state */ + int32_t ukbd; /* evdev user input */ + int32_t umouse;/* evdev user input */ + int32_t obutt; /* previous mouse buttons */ + int32_t consk; /* last consumer page key */ bdaddr_t bdaddr;/* remote bdaddr */ uint16_t state; /* session state */ #define CLOSED 0 #define W4CTRL 1 #define W4INTR 2 #define OPEN 3 bitstr_t *keys1; /* keys map (new) */ bitstr_t *keys2; /* keys map (old) */ LIST_ENTRY(bthid_session) next; /* link to next */ }; typedef struct bthid_session bthid_session_t; typedef struct bthid_session * bthid_session_p; int32_t server_init (bthid_server_p srv); void server_shutdown (bthid_server_p srv); int32_t server_do (bthid_server_p srv); int32_t client_rescan (bthid_server_p srv); int32_t client_connect (bthid_server_p srv, int fd); bthid_session_p session_open (bthid_server_p srv, hid_device_p const d); bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr); bthid_session_p session_by_fd (bthid_server_p srv, int32_t fd); +int32_t session_run (bthid_session_p s); void session_close (bthid_session_p s); void hid_initialise (bthid_session_p s); int32_t hid_control (bthid_session_p s, uint8_t *data, int32_t len); int32_t hid_interrupt (bthid_session_p s, uint8_t *data, int32_t len); #endif /* ndef _BTHIDD_H_ */ Index: head/usr.sbin/bluetooth/bthidd/btuinput.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/btuinput.c (nonexistent) +++ head/usr.sbin/bluetooth/bthidd/btuinput.c (revision 333113) @@ -0,0 +1,618 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2015-2017 Vladimir Kondratyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#define L2CAP_SOCKET_CHECKED +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bthid_config.h" +#include "bthidd.h" +#include "btuinput.h" + +static int16_t const mbuttons[8] = { + BTN_LEFT, + BTN_MIDDLE, + BTN_RIGHT, + BTN_SIDE, + BTN_EXTRA, + BTN_FORWARD, + BTN_BACK, + BTN_TASK +}; + +static uint16_t const led_codes[3] = { + LED_CAPSL, /* CLKED */ + LED_NUML, /* NLKED */ + LED_SCROLLL, /* SLKED */ +}; + +#define NONE KEY_RESERVED + +static uint16_t const keymap[0x100] = { + /* 0x00 - 0x27 */ + NONE, NONE, NONE, NONE, KEY_A, KEY_B, KEY_C, KEY_D, + KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, + KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, + KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, + KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, + /* 0x28 - 0x3f */ + KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, + KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, + KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, + KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, + KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, + KEY_F3, KEY_F4, KEY_F5, KEY_F6, + /* 0x40 - 0x5f */ + KEY_F7, KEY_F8, KEY_F9, KEY_F10, + KEY_F11, KEY_F12, KEY_SYSRQ, KEY_SCROLLLOCK, + KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, + KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, + KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, + KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, + KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, + KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, + /* 0x60 - 0x7f */ + KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, + KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, + KEY_F13, KEY_F14, KEY_F15, KEY_F16, + KEY_F17, KEY_F18, KEY_F19, KEY_F20, + KEY_F21, KEY_F22, KEY_F23, KEY_F24, + KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, + KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, + KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE, + /* 0x80 - 0x9f */ + KEY_VOLUMEUP, KEY_VOLUMEDOWN, NONE, NONE, + NONE, KEY_KPCOMMA, NONE, KEY_RO, + KEY_KATAKANAHIRAGANA, KEY_YEN,KEY_HENKAN, KEY_MUHENKAN, + KEY_KPJPCOMMA, NONE, NONE, NONE, + KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, + KEY_ZENKAKUHANKAKU, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + /* 0xa0 - 0xbf */ + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + /* 0xc0 - 0xdf */ + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + /* 0xe0 - 0xff */ + KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, + KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA, + KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,KEY_NEXTSONG, + KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, + KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, + KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, + KEY_SLEEP, KEY_COFFEE, KEY_REFRESH, KEY_CALC, + NONE, NONE, NONE, NONE, +}; + +/* Consumer page usage mapping */ +static uint16_t const consmap[0x300] = { + [0x030] = KEY_POWER, + [0x031] = KEY_RESTART, + [0x032] = KEY_SLEEP, + [0x034] = KEY_SLEEP, + [0x035] = KEY_KBDILLUMTOGGLE, + [0x036] = BTN_MISC, + [0x040] = KEY_MENU, + [0x041] = KEY_SELECT, + [0x042] = KEY_UP, + [0x043] = KEY_DOWN, + [0x044] = KEY_LEFT, + [0x045] = KEY_RIGHT, + [0x046] = KEY_ESC, + [0x047] = KEY_KPPLUS, + [0x048] = KEY_KPMINUS, + [0x060] = KEY_INFO, + [0x061] = KEY_SUBTITLE, + [0x063] = KEY_VCR, + [0x065] = KEY_CAMERA, + [0x069] = KEY_RED, + [0x06a] = KEY_GREEN, + [0x06b] = KEY_BLUE, + [0x06c] = KEY_YELLOW, + [0x06d] = KEY_ZOOM, + [0x06f] = KEY_BRIGHTNESSUP, + [0x070] = KEY_BRIGHTNESSDOWN, + [0x072] = KEY_BRIGHTNESS_TOGGLE, + [0x073] = KEY_BRIGHTNESS_MIN, + [0x074] = KEY_BRIGHTNESS_MAX, + [0x075] = KEY_BRIGHTNESS_AUTO, + [0x082] = KEY_VIDEO_NEXT, + [0x083] = KEY_LAST, + [0x084] = KEY_ENTER, + [0x088] = KEY_PC, + [0x089] = KEY_TV, + [0x08a] = KEY_WWW, + [0x08b] = KEY_DVD, + [0x08c] = KEY_PHONE, + [0x08d] = KEY_PROGRAM, + [0x08e] = KEY_VIDEOPHONE, + [0x08f] = KEY_GAMES, + [0x090] = KEY_MEMO, + [0x091] = KEY_CD, + [0x092] = KEY_VCR, + [0x093] = KEY_TUNER, + [0x094] = KEY_EXIT, + [0x095] = KEY_HELP, + [0x096] = KEY_TAPE, + [0x097] = KEY_TV2, + [0x098] = KEY_SAT, + [0x09a] = KEY_PVR, + [0x09c] = KEY_CHANNELUP, + [0x09d] = KEY_CHANNELDOWN, + [0x0a0] = KEY_VCR2, + [0x0b0] = KEY_PLAY, + [0x0b1] = KEY_PAUSE, + [0x0b2] = KEY_RECORD, + [0x0b3] = KEY_FASTFORWARD, + [0x0b4] = KEY_REWIND, + [0x0b5] = KEY_NEXTSONG, + [0x0b6] = KEY_PREVIOUSSONG, + [0x0b7] = KEY_STOPCD, + [0x0b8] = KEY_EJECTCD, + [0x0bc] = KEY_MEDIA_REPEAT, + [0x0b9] = KEY_SHUFFLE, + [0x0bf] = KEY_SLOW, + [0x0cd] = KEY_PLAYPAUSE, + [0x0cf] = KEY_VOICECOMMAND, + [0x0e2] = KEY_MUTE, + [0x0e5] = KEY_BASSBOOST, + [0x0e9] = KEY_VOLUMEUP, + [0x0ea] = KEY_VOLUMEDOWN, + [0x0f5] = KEY_SLOW, + [0x181] = KEY_BUTTONCONFIG, + [0x182] = KEY_BOOKMARKS, + [0x183] = KEY_CONFIG, + [0x184] = KEY_WORDPROCESSOR, + [0x185] = KEY_EDITOR, + [0x186] = KEY_SPREADSHEET, + [0x187] = KEY_GRAPHICSEDITOR, + [0x188] = KEY_PRESENTATION, + [0x189] = KEY_DATABASE, + [0x18a] = KEY_MAIL, + [0x18b] = KEY_NEWS, + [0x18c] = KEY_VOICEMAIL, + [0x18d] = KEY_ADDRESSBOOK, + [0x18e] = KEY_CALENDAR, + [0x18f] = KEY_TASKMANAGER, + [0x190] = KEY_JOURNAL, + [0x191] = KEY_FINANCE, + [0x192] = KEY_CALC, + [0x193] = KEY_PLAYER, + [0x194] = KEY_FILE, + [0x196] = KEY_WWW, + [0x199] = KEY_CHAT, + [0x19c] = KEY_LOGOFF, + [0x19e] = KEY_COFFEE, + [0x19f] = KEY_CONTROLPANEL, + [0x1a2] = KEY_APPSELECT, + [0x1a3] = KEY_NEXT, + [0x1a4] = KEY_PREVIOUS, + [0x1a6] = KEY_HELP, + [0x1a7] = KEY_DOCUMENTS, + [0x1ab] = KEY_SPELLCHECK, + [0x1ae] = KEY_KEYBOARD, + [0x1b1] = KEY_SCREENSAVER, + [0x1b4] = KEY_FILE, + [0x1b6] = KEY_IMAGES, + [0x1b7] = KEY_AUDIO, + [0x1b8] = KEY_VIDEO, + [0x1bc] = KEY_MESSENGER, + [0x1bd] = KEY_INFO, + [0x201] = KEY_NEW, + [0x202] = KEY_OPEN, + [0x203] = KEY_CLOSE, + [0x204] = KEY_EXIT, + [0x207] = KEY_SAVE, + [0x208] = KEY_PRINT, + [0x209] = KEY_PROPS, + [0x21a] = KEY_UNDO, + [0x21b] = KEY_COPY, + [0x21c] = KEY_CUT, + [0x21d] = KEY_PASTE, + [0x21f] = KEY_FIND, + [0x221] = KEY_SEARCH, + [0x222] = KEY_GOTO, + [0x223] = KEY_HOMEPAGE, + [0x224] = KEY_BACK, + [0x225] = KEY_FORWARD, + [0x226] = KEY_STOP, + [0x227] = KEY_REFRESH, + [0x22a] = KEY_BOOKMARKS, + [0x22d] = KEY_ZOOMIN, + [0x22e] = KEY_ZOOMOUT, + [0x22f] = KEY_ZOOMRESET, + [0x233] = KEY_SCROLLUP, + [0x234] = KEY_SCROLLDOWN, + [0x23d] = KEY_EDIT, + [0x25f] = KEY_CANCEL, + [0x269] = KEY_INSERT, + [0x26a] = KEY_DELETE, + [0x279] = KEY_REDO, + [0x289] = KEY_REPLY, + [0x28b] = KEY_FORWARDMAIL, + [0x28c] = KEY_SEND, + [0x2c7] = KEY_KBDINPUTASSIST_PREV, + [0x2c8] = KEY_KBDINPUTASSIST_NEXT, + [0x2c9] = KEY_KBDINPUTASSIST_PREVGROUP, + [0x2ca] = KEY_KBDINPUTASSIST_NEXTGROUP, + [0x2cb] = KEY_KBDINPUTASSIST_ACCEPT, + [0x2cc] = KEY_KBDINPUTASSIST_CANCEL, +}; + +static int32_t +uinput_open_common(hid_device_p const p, bdaddr_p local, const uint8_t *name) +{ + struct uinput_setup uisetup; + uint8_t phys[UINPUT_MAX_NAME_SIZE]; + uint8_t uniq[UINPUT_MAX_NAME_SIZE]; + int32_t fd; + + /* Take local and remote bdaddr */ + bt_ntoa(local, phys); + bt_ntoa(&p->bdaddr, uniq); + + /* Take device name from bthidd.conf. Fallback to generic name. */ + if (p->name != NULL) + name = p->name; + + /* Set device name and bus/vendor information */ + memset(&uisetup, 0, sizeof(uisetup)); + snprintf(uisetup.name, UINPUT_MAX_NAME_SIZE, + "%s, bdaddr %s", name, uniq); + uisetup.id.bustype = BUS_BLUETOOTH; + uisetup.id.vendor = p->vendor_id; + uisetup.id.product = p->product_id; + uisetup.id.version = p->version; + + fd = open("/dev/uinput", O_RDWR | O_NONBLOCK); + + if (ioctl(fd, UI_SET_PHYS, phys) < 0 || + ioctl(fd, UI_SET_BSDUNIQ, uniq) < 0 || + ioctl(fd, UI_DEV_SETUP, &uisetup) < 0) + return (-1); + + return (fd); +} + +/* + * Setup uinput device as 8button mouse with wheel(s) + * TODO: bring in more feature detection code from ums + */ +int32_t +uinput_open_mouse(hid_device_p const p, bdaddr_p local) +{ + size_t i; + int32_t fd; + + assert(p != NULL); + + if ((fd = uinput_open_common(p, local, "Bluetooth Mouse")) < 0) + goto bail_out; + + /* Advertise events and axes */ + if (ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_REL) < 0 || + ioctl(fd, UI_SET_RELBIT, REL_X) < 0 || + ioctl(fd, UI_SET_RELBIT, REL_Y) < 0 || + (p->has_wheel && ioctl(fd, UI_SET_RELBIT, REL_WHEEL) < 0) || + (p->has_hwheel && ioctl(fd, UI_SET_RELBIT, REL_HWHEEL) < 0) || + ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_POINTER) < 0) + goto bail_out; + + /* Advertise mouse buttons */ + for (i = 0; i < nitems(mbuttons); i++) + if (ioctl(fd, UI_SET_KEYBIT, mbuttons[i]) < 0) + goto bail_out; + + if (ioctl(fd, UI_DEV_CREATE) >= 0) + return (fd); /* SUCCESS */ + +bail_out: + if (fd >= 0) + close(fd); + return (-1); +} + +/* + * Setup uinput keyboard + */ +int32_t +uinput_open_keyboard(hid_device_p const p, bdaddr_p local) +{ + size_t i; + int32_t fd; + + assert(p != NULL); + + if ((fd = uinput_open_common(p, local, "Bluetooth Keyboard")) < 0) + goto bail_out; + + /* Advertise key events and LEDs */ + if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_LED) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_REP) < 0 || + ioctl(fd, UI_SET_LEDBIT, LED_CAPSL) < 0 || + ioctl(fd, UI_SET_LEDBIT, LED_NUML) < 0 || + ioctl(fd, UI_SET_LEDBIT, LED_SCROLLL)) + goto bail_out; + + /* Advertise keycodes */ + for (i = 0; i < nitems(keymap); i++) + if (keymap[i] != NONE && + ioctl(fd, UI_SET_KEYBIT, keymap[i]) < 0) + goto bail_out; + + /* Advertise consumer page keys if any */ + if (p->has_cons) { + for (i = 0; i < nitems(consmap); i++) { + if (consmap[i] != NONE && + ioctl(fd, UI_SET_KEYBIT, consmap[i]) < 0) + goto bail_out; + } + } + + if (ioctl(fd, UI_DEV_CREATE) >= 0) + return (fd); /* SUCCESS */ + +bail_out: + if (fd >= 0) + close(fd); + return (-1); +} + +/* from sys/dev/evdev/evdev.h */ +#define EVDEV_RCPT_HW_MOUSE (1<<2) +#define EVDEV_RCPT_HW_KBD (1<<3) + +#define MASK_POLL_INTERVAL 5 /* seconds */ +#define MASK_SYSCTL "kern.evdev.rcpt_mask" + +static int32_t +uinput_get_rcpt_mask(void) +{ + static struct timespec last = { 0, 0 }; + struct timespec now; + static int32_t mask = 0; + size_t len; + time_t elapsed; + + if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == -1) + return mask; + + elapsed = now.tv_sec - last.tv_sec; + if (now.tv_nsec < last.tv_nsec) + elapsed--; + + if (elapsed >= MASK_POLL_INTERVAL) { + len = sizeof(mask); + if (sysctlbyname(MASK_SYSCTL, &mask, &len, NULL, 0) < 0) { + if (errno == ENOENT) + /* kernel is compiled w/o EVDEV_SUPPORT */ + mask = EVDEV_RCPT_HW_MOUSE | EVDEV_RCPT_HW_KBD; + else + mask = 0; + } + last = now; + } + return mask; +} + +static int32_t +uinput_write_event(int32_t fd, uint16_t type, uint16_t code, int32_t value) +{ + struct input_event ie; + + assert(fd >= 0); + + memset(&ie, 0, sizeof(ie)); + ie.type = type; + ie.code = code; + ie.value = value; + return (write(fd, &ie, sizeof(ie))); +} + +int32_t +uinput_rep_mouse(int32_t fd, int32_t x, int32_t y, int32_t z, int32_t t, + int32_t buttons, int32_t obuttons) +{ + size_t i; + int32_t rcpt_mask, mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_MOUSE)) + return (0); + + if ((x != 0 && uinput_write_event(fd, EV_REL, REL_X, x) < 0) || + (y != 0 && uinput_write_event(fd, EV_REL, REL_Y, y) < 0) || + (z != 0 && uinput_write_event(fd, EV_REL, REL_WHEEL, -z) < 0) || + (t != 0 && uinput_write_event(fd, EV_REL, REL_HWHEEL, t) < 0)) + return (-1); + + for (i = 0; i < nitems(mbuttons); i++) { + mask = 1 << i; + if ((buttons & mask) == (obuttons & mask)) + continue; + if (uinput_write_event(fd, EV_KEY, mbuttons[i], + (buttons & mask) != 0) < 0) + return (-1); + } + + if (uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) < 0) + return (-1); + + return (0); +} + +/* + * Translate and report keyboard page key events + */ +int32_t +uinput_rep_key(int32_t fd, int32_t key, int32_t make) +{ + int32_t rcpt_mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_KBD)) + return (0); + + if (key >= 0 && key < (int32_t)nitems(keymap) && + keymap[key] != NONE) { + if (uinput_write_event(fd, EV_KEY, keymap[key], make) > 0 && + uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0) + return (0); + } + return (-1); +} + +/* + * Translate and report consumer page key events + */ +int32_t +uinput_rep_cons(int32_t fd, int32_t key, int32_t make) +{ + int32_t rcpt_mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_KBD)) + return (0); + + if (key >= 0 && key < (int32_t)nitems(consmap) && + consmap[key] != NONE) { + if (uinput_write_event(fd, EV_KEY, consmap[key], make) > 0 && + uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0) + return (0); + } + return (-1); +} + +/* + * Translate and report LED events + */ +int32_t +uinput_rep_leds(int32_t fd, int state, int mask) +{ + size_t i; + int32_t rcpt_mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_KBD)) + return (0); + + for (i = 0; i < nitems(led_codes); i++) { + if (mask & (1 << i) && + uinput_write_event(fd, EV_LED, led_codes[i], + state & (1 << i) ? 1 : 0) < 0) + return (-1); + } + + return (0); +} + +/* + * Process status change from evdev + */ +int32_t +uinput_kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len) +{ + struct input_event ie; + int32_t leds, oleds; + size_t i; + + assert(s != NULL); + assert(s->vkbd >= 0); + assert(len == sizeof(struct input_event)); + + memcpy(&ie, data, sizeof(ie)); + switch (ie.type) { + case EV_LED: + ioctl(s->vkbd, KDGETLED, &oleds); + leds = oleds; + for (i = 0; i < nitems(led_codes); i++) { + if (led_codes[i] == ie.code) { + if (ie.value) + leds |= 1 << i; + else + leds &= ~(1 << i); + if (leds != oleds) + ioctl(s->vkbd, KDSETLED, leds); + break; + } + } + break; + case EV_REP: + /* FALLTHROUGH. Repeats are handled by evdev subsystem */ + default: + break; + } + + return (0); +} Property changes on: head/usr.sbin/bluetooth/bthidd/btuinput.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/usr.sbin/bluetooth/bthidd/btuinput.h =================================================================== --- head/usr.sbin/bluetooth/bthidd/btuinput.h (nonexistent) +++ head/usr.sbin/bluetooth/bthidd/btuinput.h (revision 333113) @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2015-2017 Vladimir Kondratyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _UINPUT_H_ +#define _UINPUT_H_ + +int32_t uinput_open_mouse(hid_device_p const d, bdaddr_p local); +int32_t uinput_open_keyboard(hid_device_p const d, bdaddr_p local); +int32_t uinput_rep_mouse(int32_t fd, int32_t x, int32_t y, int32_t z, + int32_t t, int32_t buttons, int32_t obuttons); +int32_t uinput_rep_key(int32_t fd, int32_t key, int32_t make); +int32_t uinput_rep_cons(int32_t fd, int32_t key, int32_t make); +int32_t uinput_rep_leds(int32_t fd, int state, int mask); +int32_t uinput_kbd_status_changed(bthid_session_p s, uint8_t *data, + int32_t len); + +#endif /* ndef _UINPUT_H_ */ Property changes on: head/usr.sbin/bluetooth/bthidd/btuinput.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/usr.sbin/bluetooth/bthidd/client.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/client.c (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/client.c (revision 333113) @@ -1,261 +1,258 @@ /* * client.c */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: client.c,v 1.7 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" static int32_t client_socket(bdaddr_p bdaddr, uint16_t psm); /* * Get next config entry and create outbound connection (if required) * * XXX Do only one device at a time. At least one of my devices (3COM * Bluetooth PCCARD) rejects Create_Connection command if another * Create_Connection command is still pending. Weird... */ static int32_t connect_in_progress = 0; int32_t client_rescan(bthid_server_p srv) { static hid_device_p d; bthid_session_p s; assert(srv != NULL); if (connect_in_progress) return (0); /* another connect is still pending */ d = get_next_hid_device(d); if (d == NULL) return (0); /* XXX should not happen? empty config? */ if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL) return (0); /* session already active */ if (!d->new_device) { if (d->reconnect_initiate) return (0); /* device will initiate reconnect */ } syslog(LOG_NOTICE, "Opening outbound session for %s " \ "(new_device=%d, reconnect_initiate=%d)", bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate); if ((s = session_open(srv, d)) == NULL) { syslog(LOG_CRIT, "Could not create outbound session for %s", bt_ntoa(&d->bdaddr, NULL)); return (-1); } /* Open control channel */ s->ctrl = client_socket(&s->bdaddr, d->control_psm); if (s->ctrl < 0) { syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); session_close(s); return (-1); } s->state = W4CTRL; FD_SET(s->ctrl, &srv->wfdset); if (s->ctrl > srv->maxfd) srv->maxfd = s->ctrl; connect_in_progress = 1; return (0); } /* * Process connect on the socket */ int32_t client_connect(bthid_server_p srv, int32_t fd) { bthid_session_p s; hid_device_p d; int32_t error; socklen_t len; assert(srv != NULL); assert(fd >= 0); s = session_by_fd(srv, fd); assert(s != NULL); d = get_hid_device(&s->bdaddr); assert(d != NULL); error = 0; len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); session_close(s); connect_in_progress = 0; return (-1); } if (error != 0) { syslog(LOG_ERR, "Could not connect to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(error), error); session_close(s); connect_in_progress = 0; return (0); } switch (s->state) { case W4CTRL: /* Control channel is open */ assert(s->ctrl == fd); assert(s->intr == -1); /* Open interrupt channel */ s->intr = client_socket(&s->bdaddr, d->interrupt_psm); if (s->intr < 0) { syslog(LOG_ERR, "Could not open interrupt channel " \ "to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); session_close(s); connect_in_progress = 0; return (-1); } s->state = W4INTR; FD_SET(s->intr, &srv->wfdset); if (s->intr > srv->maxfd) srv->maxfd = s->intr; d->new_device = 0; /* reset new device flag */ write_hids_file(); break; case W4INTR: /* Interrupt channel is open */ assert(s->ctrl != -1); assert(s->intr == fd); s->state = OPEN; connect_in_progress = 0; - /* Register session's vkbd descriptor (if any) for read */ - if (s->state == OPEN && d->keyboard) { - assert(s->vkbd != -1); - - FD_SET(s->vkbd, &srv->rfdset); - if (s->vkbd > srv->maxfd) - srv->maxfd = s->vkbd; - } + /* Create kbd/mouse after both channels are established */ + if (session_run(s) < 0) { + session_close(s); + return (-1); + } break; default: assert(0); break; } /* Move fd to from the write fd set into read fd set */ FD_CLR(fd, &srv->wfdset); FD_SET(fd, &srv->rfdset); return (0); } /* * Create bound non-blocking socket and initiate connect */ static int client_socket(bdaddr_p bdaddr, uint16_t psm) { struct sockaddr_l2cap l2addr; int32_t s, m; s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (s < 0) return (-1); m = fcntl(s, F_GETFL); if (m < 0) { close(s); return (-1); } if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) { close(s); return (-1); } l2addr.l2cap_len = sizeof(l2addr); l2addr.l2cap_family = AF_BLUETOOTH; memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr)); l2addr.l2cap_psm = 0; l2addr.l2cap_bdaddr_type = BDADDR_BREDR; l2addr.l2cap_cid = 0; if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { close(s); return (-1); } memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr)); l2addr.l2cap_psm = htole16(psm); if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 && errno != EINPROGRESS) { close(s); return (-1); } return (s); } Index: head/usr.sbin/bluetooth/bthidd/hid.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/hid.c (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/hid.c (revision 333113) @@ -1,557 +1,579 @@ /* * hid.c */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: hid.c,v 1.5 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #include #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" +#include "btuinput.h" #include "kbd.h" /* * Inoffical and unannounced report ids for Apple Mice and trackpad */ #define TRACKPAD_REPORT_ID 0x28 #define AMM_REPORT_ID 0x29 #define BATT_STAT_REPORT_ID 0x30 #define BATT_STRENGTH_REPORT_ID 0x47 #define SURFACE_REPORT_ID 0x61 /* * Apple magic mouse (AMM) specific device state */ #define AMM_MAX_BUTTONS 16 struct apple_state { int y [AMM_MAX_BUTTONS]; int button_state; }; #define MAGIC_MOUSE(D) (((D)->vendor_id == 0x5ac) && ((D)->product_id == 0x30d)) #define AMM_BASIC_BLOCK 5 #define AMM_FINGER_BLOCK 8 #define AMM_VALID_REPORT(L) (((L) >= AMM_BASIC_BLOCK) && \ ((L) <= 16*AMM_FINGER_BLOCK + AMM_BASIC_BLOCK) && \ ((L) % AMM_FINGER_BLOCK) == AMM_BASIC_BLOCK) #define AMM_WHEEL_SPEED 100 /* * Probe for per-device initialisation */ void hid_initialise(bthid_session_p s) { hid_device_p hid_device = get_hid_device(&s->bdaddr); if (hid_device && MAGIC_MOUSE(hid_device)) { /* Magic report to enable trackpad on Apple's Magic Mouse */ static uint8_t rep[] = {0x53, 0xd7, 0x01}; if ((s->ctx = calloc(1, sizeof(struct apple_state))) == NULL) return; write(s->ctrl, rep, 3); } } /* * Process data from control channel */ int32_t hid_control(bthid_session_p s, uint8_t *data, int32_t len) { assert(s != NULL); assert(data != NULL); assert(len > 0); switch (data[0] >> 4) { case 0: /* Handshake (response to command) */ if (data[0] & 0xf) syslog(LOG_ERR, "Got handshake message with error " \ "response 0x%x from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); break; case 1: /* HID Control */ switch (data[0] & 0xf) { case 0: /* NOP */ break; case 1: /* Hard reset */ case 2: /* Soft reset */ syslog(LOG_WARNING, "Device %s requested %s reset", bt_ntoa(&s->bdaddr, NULL), ((data[0] & 0xf) == 1)? "hard" : "soft"); break; case 3: /* Suspend */ syslog(LOG_NOTICE, "Device %s requested Suspend", bt_ntoa(&s->bdaddr, NULL)); break; case 4: /* Exit suspend */ syslog(LOG_NOTICE, "Device %s requested Exit Suspend", bt_ntoa(&s->bdaddr, NULL)); break; case 5: /* Virtual cable unplug */ syslog(LOG_NOTICE, "Device %s unplugged virtual cable", bt_ntoa(&s->bdaddr, NULL)); session_close(s); break; default: syslog(LOG_WARNING, "Device %s sent unknown " \ "HID_Control message 0x%x", bt_ntoa(&s->bdaddr, NULL), data[0]); break; } break; default: syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \ "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); break; } return (0); } /* * Process data from the interrupt channel */ int32_t hid_interrupt(bthid_session_p s, uint8_t *data, int32_t len) { hid_device_p hid_device; hid_data_t d; hid_item_t h; int32_t report_id, usage, page, val, mouse_x, mouse_y, mouse_z, mouse_t, mouse_butt, mevents, kevents, i; assert(s != NULL); assert(s->srv != NULL); assert(data != NULL); if (len < 3) { syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \ "channel from %s", len, bt_ntoa(&s->bdaddr, NULL)); return (-1); } if (data[0] != 0xa1) { syslog(LOG_ERR, "Got unexpected message 0x%x on " \ "Interrupt channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); return (-1); } report_id = data[1]; data ++; len --; hid_device = get_hid_device(&s->bdaddr); assert(hid_device != NULL); mouse_x = mouse_y = mouse_z = mouse_t = mouse_butt = 0; mevents = kevents = 0; for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); hid_get_item(d, &h) > 0; ) { if ((h.flags & HIO_CONST) || (h.report_ID != report_id) || (h.kind != hid_input)) continue; page = HID_PAGE(h.usage); val = hid_get_data(data, &h); /* * When the input field is an array and the usage is specified * with a range instead of an ID, we have to derive the actual * usage by using the item value as an index in the usage range * list. */ if ((h.flags & HIO_VARIABLE)) { usage = HID_USAGE(h.usage); } else { const uint32_t usage_offset = val - h.logical_minimum; usage = HID_USAGE(h.usage_minimum + usage_offset); } switch (page) { case HUP_GENERIC_DESKTOP: switch (usage) { case HUG_X: mouse_x = val; mevents ++; break; case HUG_Y: mouse_y = val; mevents ++; break; case HUG_WHEEL: mouse_z = -val; mevents ++; break; case HUG_SYSTEM_SLEEP: if (val) syslog(LOG_NOTICE, "Sleep button pressed"); break; } break; case HUP_KEYBOARD: kevents ++; if (h.flags & HIO_VARIABLE) { if (val && usage < kbd_maxkey()) bit_set(s->keys1, usage); } else { if (val && val < kbd_maxkey()) bit_set(s->keys1, val); for (i = 1; i < h.report_count; i++) { h.pos += h.report_size; val = hid_get_data(data, &h); if (val && val < kbd_maxkey()) bit_set(s->keys1, val); } } break; case HUP_BUTTON: if (usage != 0) { if (usage == 2) usage = 3; else if (usage == 3) usage = 2; mouse_butt |= (val << (usage - 1)); mevents ++; } break; case HUP_CONSUMER: + if (hid_device->keyboard && s->srv->uinput) { + if (h.flags & HIO_VARIABLE) { + uinput_rep_cons(s->ukbd, usage, !!val); + } else { + if (s->consk > 0) + uinput_rep_cons(s->ukbd, + s->consk, 0); + if (uinput_rep_cons(s->ukbd, val, 1) + == 0) + s->consk = val; + } + } + if (!val) break; switch (usage) { case HUC_AC_PAN: /* Horizontal scroll */ mouse_t = val; mevents ++; val = 0; break; case 0xb5: /* Scan Next Track */ val = 0x19; break; case 0xb6: /* Scan Previous Track */ val = 0x10; break; case 0xb7: /* Stop */ val = 0x24; break; case 0xcd: /* Play/Pause */ val = 0x22; break; case 0xe2: /* Mute */ val = 0x20; break; case 0xe9: /* Volume Up */ val = 0x30; break; case 0xea: /* Volume Down */ val = 0x2E; break; case 0x183: /* Media Select */ val = 0x6D; break; case 0x018a: /* Mail */ val = 0x6C; break; case 0x192: /* Calculator */ val = 0x21; break; case 0x194: /* My Computer */ val = 0x6B; break; case 0x221: /* WWW Search */ val = 0x65; break; case 0x223: /* WWW Home */ val = 0x32; break; case 0x224: /* WWW Back */ val = 0x6A; break; case 0x225: /* WWW Forward */ val = 0x69; break; case 0x226: /* WWW Stop */ val = 0x68; break; case 0x227: /* WWW Refresh */ val = 0x67; break; case 0x22a: /* WWW Favorites */ val = 0x66; break; default: val = 0; break; } /* XXX FIXME - UGLY HACK */ if (val != 0) { if (hid_device->keyboard) { int32_t buf[4] = { 0xe0, val, 0xe0, val|0x80 }; assert(s->vkbd != -1); write(s->vkbd, buf, sizeof(buf)); } else syslog(LOG_ERR, "Keyboard events " \ "received from non-keyboard " \ "device %s. Please report", bt_ntoa(&s->bdaddr, NULL)); } break; case HUP_MICROSOFT: switch (usage) { case 0xfe01: if (!hid_device->battery_power) break; switch (val) { case 1: syslog(LOG_INFO, "Battery is OK on %s", bt_ntoa(&s->bdaddr, NULL)); break; case 2: syslog(LOG_NOTICE, "Low battery on %s", bt_ntoa(&s->bdaddr, NULL)); break; case 3: syslog(LOG_WARNING, "Very low battery "\ "on %s", bt_ntoa(&s->bdaddr, NULL)); break; } break; } break; } } hid_end_parse(d); /* * Apple adheres to no standards and sends reports it does * not introduce in its hid descriptor for its magic mouse. * Handle those reports here. */ if (MAGIC_MOUSE(hid_device) && s->ctx) { struct apple_state *c = (struct apple_state *)s->ctx; int firm = 0, middle = 0; int16_t v; data++, len--; /* Chomp report_id */ if (report_id != AMM_REPORT_ID || !AMM_VALID_REPORT(len)) goto check_middle_button; /* * The basics. When touches are detected, no normal mouse * reports are sent. Collect clicks and dx/dy */ if (data[2] & 1) mouse_butt |= 0x1; if (data[2] & 2) mouse_butt |= 0x4; if ((v = data[0] + ((data[2] & 0x0C) << 6))) mouse_x += ((int16_t)(v << 6)) >> 6, mevents++; if ((v = data[1] + ((data[2] & 0x30) << 4))) mouse_y += ((int16_t)(v << 6)) >> 6, mevents++; /* * The hard part: accumulate touch events and emulate middle */ for (data += AMM_BASIC_BLOCK, len -= AMM_BASIC_BLOCK; len >= AMM_FINGER_BLOCK; data += AMM_FINGER_BLOCK, len -= AMM_FINGER_BLOCK) { int x, y, z, force, id; v = data[0] | ((data[1] & 0xf) << 8); x = ((int16_t)(v << 4)) >> 4; v = (data[1] >> 4) | (data[2] << 4); y = -(((int16_t)(v << 4)) >> 4); force = data[5] & 0x3f; id = 0xf & ((data[5] >> 6) | (data[6] << 2)); z = (y - c->y[id]) / AMM_WHEEL_SPEED; switch ((data[7] >> 4) & 0x7) { /* Phase */ case 3: /* First touch */ c->y[id] = y; break; case 4: /* Touch dragged */ if (z) { mouse_z += z; c->y[id] += z * AMM_WHEEL_SPEED; mevents++; } break; default: break; } /* Count firm touches vs. firm+middle touches */ if (force >= 8 && ++firm && x > -350 && x < 350) ++middle; } /* * If a new click is registered by mouse and there are firm * touches which are all in center, make it a middle click */ if (mouse_butt && !c->button_state && firm && middle == firm) mouse_butt = 0x2; /* * If we're still clicking and have converted the click * to a middle click, keep it middle clicking */ check_middle_button: if (mouse_butt && c->button_state == 0x2) mouse_butt = 0x2; if (mouse_butt != c->button_state) c->button_state = mouse_butt, mevents++; } /* * XXX FIXME Feed keyboard events into kernel. * The code below works, bit host also needs to track * and handle repeat. * * Key repeat currently works in X, but not in console. */ if (kevents > 0) { if (hid_device->keyboard) { assert(s->vkbd != -1); kbd_process_keys(s); } else syslog(LOG_ERR, "Keyboard events received from " \ "non-keyboard device %s. Please report", bt_ntoa(&s->bdaddr, NULL)); } /* * XXX FIXME Feed mouse events into kernel. * The code block below works, but it is not good enough. * Need to track double-clicks etc. * * Double click currently works in X, but not in console. */ if (mevents > 0) { struct mouse_info mi; memset(&mi, 0, sizeof(mi)); mi.operation = MOUSE_ACTION; mi.u.data.buttons = mouse_butt; /* translate T-axis into button presses */ if (mouse_t != 0) { mi.u.data.buttons |= 1 << (mouse_t > 0 ? 6 : 5); if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) syslog(LOG_ERR, "Could not process mouse " \ "events from %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); } mi.u.data.x = mouse_x; mi.u.data.y = mouse_y; mi.u.data.z = mouse_z; mi.u.data.buttons = mouse_butt; if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) syslog(LOG_ERR, "Could not process mouse events from " \ "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); + + if (hid_device->mouse && s->srv->uinput && + uinput_rep_mouse(s->umouse, mouse_x, mouse_y, mouse_z, + mouse_t, mouse_butt, s->obutt) < 0) + syslog(LOG_ERR, "Could not process mouse events from " \ + "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), + strerror(errno), errno); + s->obutt = mouse_butt; } return (0); } Index: head/usr.sbin/bluetooth/bthidd/kbd.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/kbd.c (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/kbd.c (revision 333113) @@ -1,584 +1,614 @@ /* * kbd.c */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: kbd.c,v 1.4 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" +#include "btuinput.h" #include "kbd.h" static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd); static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob); +static void uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd); /* * HID code to PS/2 set 1 code translation table. * * http://www.microsoft.com/whdc/device/input/Scancode.mspx * * The table only contains "make" (key pressed) codes. * The "break" (key released) code is generated as "make" | 0x80 */ #define E0PREFIX (1U << 31) #define NOBREAK (1 << 30) #define CODEMASK (~(E0PREFIX|NOBREAK)) static int32_t const x[] = { /*==================================================*/ /* Name HID code Make Break*/ /*==================================================*/ /* No Event 00 */ -1, /* None */ /* Overrun Error 01 */ NOBREAK|0xFF, /* None */ /* POST Fail 02 */ NOBREAK|0xFC, /* None */ /* ErrorUndefined 03 */ -1, /* Unassigned */ /* a A 04 */ 0x1E, /* 9E */ /* b B 05 */ 0x30, /* B0 */ /* c C 06 */ 0x2E, /* AE */ /* d D 07 */ 0x20, /* A0 */ /* e E 08 */ 0x12, /* 92 */ /* f F 09 */ 0x21, /* A1 */ /* g G 0A */ 0x22, /* A2 */ /* h H 0B */ 0x23, /* A3 */ /* i I 0C */ 0x17, /* 97 */ /* j J 0D */ 0x24, /* A4 */ /* k K 0E */ 0x25, /* A5 */ /* l L 0F */ 0x26, /* A6 */ /* m M 10 */ 0x32, /* B2 */ /* n N 11 */ 0x31, /* B1 */ /* o O 12 */ 0x18, /* 98 */ /* p P 13 */ 0x19, /* 99 */ /* q Q 14 */ 0x10, /* 90 */ /* r R 15 */ 0x13, /* 93 */ /* s S 16 */ 0x1F, /* 9F */ /* t T 17 */ 0x14, /* 94 */ /* u U 18 */ 0x16, /* 96 */ /* v V 19 */ 0x2F, /* AF */ /* w W 1A */ 0x11, /* 91 */ /* x X 1B */ 0x2D, /* AD */ /* y Y 1C */ 0x15, /* 95 */ /* z Z 1D */ 0x2C, /* AC */ /* 1 ! 1E */ 0x02, /* 82 */ /* 2 @ 1F */ 0x03, /* 83 */ /* 3 # 20 */ 0x04, /* 84 */ /* 4 $ 21 */ 0x05, /* 85 */ /* 5 % 22 */ 0x06, /* 86 */ /* 6 ^ 23 */ 0x07, /* 87 */ /* 7 & 24 */ 0x08, /* 88 */ /* 8 * 25 */ 0x09, /* 89 */ /* 9 ( 26 */ 0x0A, /* 8A */ /* 0 ) 27 */ 0x0B, /* 8B */ /* Return 28 */ 0x1C, /* 9C */ /* Escape 29 */ 0x01, /* 81 */ /* Backspace 2A */ 0x0E, /* 8E */ /* Tab 2B */ 0x0F, /* 8F */ /* Space 2C */ 0x39, /* B9 */ /* - _ 2D */ 0x0C, /* 8C */ /* = + 2E */ 0x0D, /* 8D */ /* [ { 2F */ 0x1A, /* 9A */ /* ] } 30 */ 0x1B, /* 9B */ /* \ | 31 */ 0x2B, /* AB */ /* Europe 1 32 */ 0x2B, /* AB */ /* ; : 33 */ 0x27, /* A7 */ /* " ' 34 */ 0x28, /* A8 */ /* ` ~ 35 */ 0x29, /* A9 */ /* comma < 36 */ 0x33, /* B3 */ /* . > 37 */ 0x34, /* B4 */ /* / ? 38 */ 0x35, /* B5 */ /* Caps Lock 39 */ 0x3A, /* BA */ /* F1 3A */ 0x3B, /* BB */ /* F2 3B */ 0x3C, /* BC */ /* F3 3C */ 0x3D, /* BD */ /* F4 3D */ 0x3E, /* BE */ /* F5 3E */ 0x3F, /* BF */ /* F6 3F */ 0x40, /* C0 */ /* F7 40 */ 0x41, /* C1 */ /* F8 41 */ 0x42, /* C2 */ /* F9 42 */ 0x43, /* C3 */ /* F10 43 */ 0x44, /* C4 */ /* F11 44 */ 0x57, /* D7 */ /* F12 45 */ 0x58, /* D8 */ /* Print Screen 46 */ E0PREFIX|0x37, /* E0 B7 */ /* Scroll Lock 47 */ 0x46, /* C6 */ #if 0 /* Break (Ctrl-Pause) 48 */ E0 46 E0 C6, /* None */ /* Pause 48 */ E1 1D 45 E1 9D C5, /* None */ #else /* Break (Ctrl-Pause)/Pause 48 */ NOBREAK /* Special case */, /* None */ #endif /* Insert 49 */ E0PREFIX|0x52, /* E0 D2 */ /* Home 4A */ E0PREFIX|0x47, /* E0 C7 */ /* Page Up 4B */ E0PREFIX|0x49, /* E0 C9 */ /* Delete 4C */ E0PREFIX|0x53, /* E0 D3 */ /* End 4D */ E0PREFIX|0x4F, /* E0 CF */ /* Page Down 4E */ E0PREFIX|0x51, /* E0 D1 */ /* Right Arrow 4F */ E0PREFIX|0x4D, /* E0 CD */ /* Left Arrow 50 */ E0PREFIX|0x4B, /* E0 CB */ /* Down Arrow 51 */ E0PREFIX|0x50, /* E0 D0 */ /* Up Arrow 52 */ E0PREFIX|0x48, /* E0 C8 */ /* Num Lock 53 */ 0x45, /* C5 */ /* Keypad / 54 */ E0PREFIX|0x35, /* E0 B5 */ /* Keypad * 55 */ 0x37, /* B7 */ /* Keypad - 56 */ 0x4A, /* CA */ /* Keypad + 57 */ 0x4E, /* CE */ /* Keypad Enter 58 */ E0PREFIX|0x1C, /* E0 9C */ /* Keypad 1 End 59 */ 0x4F, /* CF */ /* Keypad 2 Down 5A */ 0x50, /* D0 */ /* Keypad 3 PageDn 5B */ 0x51, /* D1 */ /* Keypad 4 Left 5C */ 0x4B, /* CB */ /* Keypad 5 5D */ 0x4C, /* CC */ /* Keypad 6 Right 5E */ 0x4D, /* CD */ /* Keypad 7 Home 5F */ 0x47, /* C7 */ /* Keypad 8 Up 60 */ 0x48, /* C8 */ /* Keypad 9 PageUp 61 */ 0x49, /* C9 */ /* Keypad 0 Insert 62 */ 0x52, /* D2 */ /* Keypad . Delete 63 */ 0x53, /* D3 */ /* Europe 2 64 */ 0x56, /* D6 */ /* App 65 */ E0PREFIX|0x5D, /* E0 DD */ /* Keyboard Power 66 */ E0PREFIX|0x5E, /* E0 DE */ /* Keypad = 67 */ 0x59, /* D9 */ /* F13 68 */ 0x64, /* E4 */ /* F14 69 */ 0x65, /* E5 */ /* F15 6A */ 0x66, /* E6 */ /* F16 6B */ 0x67, /* E7 */ /* F17 6C */ 0x68, /* E8 */ /* F18 6D */ 0x69, /* E9 */ /* F19 6E */ 0x6A, /* EA */ /* F20 6F */ 0x6B, /* EB */ /* F21 70 */ 0x6C, /* EC */ /* F22 71 */ 0x6D, /* ED */ /* F23 72 */ 0x6E, /* EE */ /* F24 73 */ 0x76, /* F6 */ /* Keyboard Execute 74 */ -1, /* Unassigned */ /* Keyboard Help 75 */ -1, /* Unassigned */ /* Keyboard Menu 76 */ -1, /* Unassigned */ /* Keyboard Select 77 */ -1, /* Unassigned */ /* Keyboard Stop 78 */ -1, /* Unassigned */ /* Keyboard Again 79 */ -1, /* Unassigned */ /* Keyboard Undo 7A */ -1, /* Unassigned */ /* Keyboard Cut 7B */ -1, /* Unassigned */ /* Keyboard Copy 7C */ -1, /* Unassigned */ /* Keyboard Paste 7D */ -1, /* Unassigned */ /* Keyboard Find 7E */ -1, /* Unassigned */ /* Keyboard Mute 7F */ -1, /* Unassigned */ /* Keyboard Volume Up 80 */ -1, /* Unassigned */ /* Keyboard Volume Dn 81 */ -1, /* Unassigned */ /* Keyboard Locking Caps Lock 82 */ -1, /* Unassigned */ /* Keyboard Locking Num Lock 83 */ -1, /* Unassigned */ /* Keyboard Locking Scroll Lock 84 */ -1, /* Unassigned */ /* Keypad comma 85 */ 0x7E, /* FE */ /* Keyboard Equal Sign 86 */ -1, /* Unassigned */ /* Keyboard Int'l 1 87 */ 0x73, /* F3 */ /* Keyboard Int'l 2 88 */ 0x70, /* F0 */ /* Keyboard Int'l 2 89 */ 0x7D, /* FD */ /* Keyboard Int'l 4 8A */ 0x79, /* F9 */ /* Keyboard Int'l 5 8B */ 0x7B, /* FB */ /* Keyboard Int'l 6 8C */ 0x5C, /* DC */ /* Keyboard Int'l 7 8D */ -1, /* Unassigned */ /* Keyboard Int'l 8 8E */ -1, /* Unassigned */ /* Keyboard Int'l 9 8F */ -1, /* Unassigned */ /* Keyboard Lang 1 90 */ 0x71, /* Kana */ /* Keyboard Lang 2 91 */ 0x72, /* Eisu */ /* Keyboard Lang 3 92 */ 0x78, /* F8 */ /* Keyboard Lang 4 93 */ 0x77, /* F7 */ /* Keyboard Lang 5 94 */ 0x76, /* F6 */ /* Keyboard Lang 6 95 */ -1, /* Unassigned */ /* Keyboard Lang 7 96 */ -1, /* Unassigned */ /* Keyboard Lang 8 97 */ -1, /* Unassigned */ /* Keyboard Lang 9 98 */ -1, /* Unassigned */ /* Keyboard Alternate Erase 99 */ -1, /* Unassigned */ /* Keyboard SysReq/Attention 9A */ -1, /* Unassigned */ /* Keyboard Cancel 9B */ -1, /* Unassigned */ /* Keyboard Clear 9C */ -1, /* Unassigned */ /* Keyboard Prior 9D */ -1, /* Unassigned */ /* Keyboard Return 9E */ -1, /* Unassigned */ /* Keyboard Separator 9F */ -1, /* Unassigned */ /* Keyboard Out A0 */ -1, /* Unassigned */ /* Keyboard Oper A1 */ -1, /* Unassigned */ /* Keyboard Clear/Again A2 */ -1, /* Unassigned */ /* Keyboard CrSel/Props A3 */ -1, /* Unassigned */ /* Keyboard ExSel A4 */ -1, /* Unassigned */ /* Reserved A5 */ -1, /* Reserved */ /* Reserved A6 */ -1, /* Reserved */ /* Reserved A7 */ -1, /* Reserved */ /* Reserved A8 */ -1, /* Reserved */ /* Reserved A9 */ -1, /* Reserved */ /* Reserved AA */ -1, /* Reserved */ /* Reserved AB */ -1, /* Reserved */ /* Reserved AC */ -1, /* Reserved */ /* Reserved AD */ -1, /* Reserved */ /* Reserved AE */ -1, /* Reserved */ /* Reserved AF */ -1, /* Reserved */ /* Reserved B0 */ -1, /* Reserved */ /* Reserved B1 */ -1, /* Reserved */ /* Reserved B2 */ -1, /* Reserved */ /* Reserved B3 */ -1, /* Reserved */ /* Reserved B4 */ -1, /* Reserved */ /* Reserved B5 */ -1, /* Reserved */ /* Reserved B6 */ -1, /* Reserved */ /* Reserved B7 */ -1, /* Reserved */ /* Reserved B8 */ -1, /* Reserved */ /* Reserved B9 */ -1, /* Reserved */ /* Reserved BA */ -1, /* Reserved */ /* Reserved BB */ -1, /* Reserved */ /* Reserved BC */ -1, /* Reserved */ /* Reserved BD */ -1, /* Reserved */ /* Reserved BE */ -1, /* Reserved */ /* Reserved BF */ -1, /* Reserved */ /* Reserved C0 */ -1, /* Reserved */ /* Reserved C1 */ -1, /* Reserved */ /* Reserved C2 */ -1, /* Reserved */ /* Reserved C3 */ -1, /* Reserved */ /* Reserved C4 */ -1, /* Reserved */ /* Reserved C5 */ -1, /* Reserved */ /* Reserved C6 */ -1, /* Reserved */ /* Reserved C7 */ -1, /* Reserved */ /* Reserved C8 */ -1, /* Reserved */ /* Reserved C9 */ -1, /* Reserved */ /* Reserved CA */ -1, /* Reserved */ /* Reserved CB */ -1, /* Reserved */ /* Reserved CC */ -1, /* Reserved */ /* Reserved CD */ -1, /* Reserved */ /* Reserved CE */ -1, /* Reserved */ /* Reserved CF */ -1, /* Reserved */ /* Reserved D0 */ -1, /* Reserved */ /* Reserved D1 */ -1, /* Reserved */ /* Reserved D2 */ -1, /* Reserved */ /* Reserved D3 */ -1, /* Reserved */ /* Reserved D4 */ -1, /* Reserved */ /* Reserved D5 */ -1, /* Reserved */ /* Reserved D6 */ -1, /* Reserved */ /* Reserved D7 */ -1, /* Reserved */ /* Reserved D8 */ -1, /* Reserved */ /* Reserved D9 */ -1, /* Reserved */ /* Reserved DA */ -1, /* Reserved */ /* Reserved DB */ -1, /* Reserved */ /* Reserved DC */ -1, /* Reserved */ /* Reserved DD */ -1, /* Reserved */ /* Reserved DE */ -1, /* Reserved */ /* Reserved DF */ -1, /* Reserved */ /* Left Control E0 */ 0x1D, /* 9D */ /* Left Shift E1 */ 0x2A, /* AA */ /* Left Alt E2 */ 0x38, /* B8 */ /* Left GUI E3 */ E0PREFIX|0x5B, /* E0 DB */ /* Right Control E4 */ E0PREFIX|0x1D, /* E0 9D */ /* Right Shift E5 */ 0x36, /* B6 */ /* Right Alt E6 */ E0PREFIX|0x38, /* E0 B8 */ /* Right GUI E7 */ E0PREFIX|0x5C /* E0 DC */ }; #define xsize ((int32_t)(sizeof(x)/sizeof(x[0]))) /* * Get a max HID keycode (aligned) */ int32_t kbd_maxkey(void) { return (xsize); } /* * Process keys */ int32_t kbd_process_keys(bthid_session_p s) { bitstr_t diff[bitstr_size(xsize)]; int32_t f1, f2, i; assert(s != NULL); assert(s->srv != NULL); /* Check if the new keys have been pressed */ bit_ffs(s->keys1, xsize, &f1); /* Check if old keys still pressed */ bit_ffs(s->keys2, xsize, &f2); if (f1 == -1) { /* no new key pressed */ if (f2 != -1) { /* release old keys */ kbd_write(s->keys2, f2, 0, s->vkbd); + uinput_kbd_write(s->keys2, f2, 0, s->ukbd); memset(s->keys2, 0, bitstr_size(xsize)); } return (0); } if (f2 == -1) { /* no old keys, but new keys pressed */ assert(f1 != -1); memcpy(s->keys2, s->keys1, bitstr_size(xsize)); kbd_write(s->keys1, f1, 1, s->vkbd); + uinput_kbd_write(s->keys1, f1, 1, s->ukbd); memset(s->keys1, 0, bitstr_size(xsize)); return (0); } /* new keys got pressed, old keys got released */ memset(diff, 0, bitstr_size(xsize)); for (i = f2; i < xsize; i ++) { if (bit_test(s->keys2, i)) { if (!bit_test(s->keys1, i)) { bit_clear(s->keys2, i); bit_set(diff, i); } } } for (i = f1; i < xsize; i++) { if (bit_test(s->keys1, i)) { if (!bit_test(s->keys2, i)) bit_set(s->keys2, i); else bit_clear(s->keys1, i); } } bit_ffs(diff, xsize, &f2); - if (f2 > 0) + if (f2 > 0) { kbd_write(diff, f2, 0, s->vkbd); + uinput_kbd_write(diff, f2, 0, s->ukbd); + } bit_ffs(s->keys1, xsize, &f1); if (f1 > 0) { kbd_write(s->keys1, f1, 1, s->vkbd); + uinput_kbd_write(s->keys1, f1, 1, s->ukbd); memset(s->keys1, 0, bitstr_size(xsize)); } return (0); } /* * Translate given keymap and write keyscodes + */ +void +uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd) +{ + int32_t i; + + if (fd >= 0) { + for (i = fb; i < xsize; i++) { + if (bit_test(m, i)) + uinput_rep_key(fd, i, make); + } + } +} + +/* + * Translate given keymap and write keyscodes */ static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd) { int32_t i, *b, *eob, n, buf[64]; b = buf; eob = b + sizeof(buf)/sizeof(buf[0]); i = fb; while (i < xsize) { if (bit_test(m, i)) { n = kbd_xlate(i, make, b, eob); if (n == -1) { write(fd, buf, (b - buf) * sizeof(buf[0])); b = buf; continue; } b += n; } i ++; } if (b != buf) write(fd, buf, (b - buf) * sizeof(buf[0])); } /* * Translate HID code into PS/2 code and put codes into buffer b. * Returns the number of codes put in b. Return -1 if buffer has not * enough space. */ #undef PUT #define PUT(c, n, b, eob) \ do { \ if ((b) >= (eob)) \ return (-1); \ *(b) = (c); \ (b) ++; \ (n) ++; \ } while (0) static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob) { int32_t c, n; n = 0; if (code >= xsize) return (0); /* HID code is not in the table */ /* Handle special case - Pause/Break */ if (code == 0x48) { if (!make) return (0); /* No break code */ #if 0 XXX FIXME if (ctrl_is_pressed) { /* Break (Ctrl-Pause) */ PUT(0xe0, n, b, eob); PUT(0x46, n, b, eob); PUT(0xe0, n, b, eob); PUT(0xc6, n, b, eob); } else { /* Pause */ PUT(0xe1, n, b, eob); PUT(0x1d, n, b, eob); PUT(0x45, n, b, eob); PUT(0xe1, n, b, eob); PUT(0x9d, n, b, eob); PUT(0xc5, n, b, eob); } #endif return (n); } if ((c = x[code]) == -1) return (0); /* HID code translation is not defined */ if (make) { if (c & E0PREFIX) PUT(0xe0, n, b, eob); PUT((c & CODEMASK), n, b, eob); } else if (!(c & NOBREAK)) { if (c & E0PREFIX) PUT(0xe0, n, b, eob); PUT((0x80|(c & CODEMASK)), n, b, eob); } return (n); } /* * Process status change from vkbd(4) */ int32_t kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len) { vkbd_status_t st; uint8_t found, report_id; hid_device_p hid_device; hid_data_t d; hid_item_t h; + uint8_t leds_mask = 0; assert(s != NULL); assert(len == sizeof(vkbd_status_t)); memcpy(&st, data, sizeof(st)); found = 0; report_id = NO_REPORT_ID; hid_device = get_hid_device(&s->bdaddr); assert(hid_device != NULL); data[0] = 0xa2; /* DATA output (HID output report) */ data[1] = 0x00; data[2] = 0x00; for (d = hid_start_parse(hid_device->desc, 1 << hid_output, -1); hid_get_item(d, &h) > 0; ) { if (HID_PAGE(h.usage) == HUP_LEDS) { found++; if (report_id == NO_REPORT_ID) report_id = h.report_ID; else if (h.report_ID != report_id) syslog(LOG_WARNING, "Output HID report IDs " \ "for %s do not match: %d vs. %d. " \ "Please report", bt_ntoa(&s->bdaddr, NULL), h.report_ID, report_id); switch(HID_USAGE(h.usage)) { case 0x01: /* Num Lock LED */ if (st.leds & LED_NUM) hid_set_data(&data[1], &h, 1); + leds_mask |= LED_NUM; break; case 0x02: /* Caps Lock LED */ if (st.leds & LED_CAP) hid_set_data(&data[1], &h, 1); + leds_mask |= LED_CAP; break; case 0x03: /* Scroll Lock LED */ if (st.leds & LED_SCR) hid_set_data(&data[1], &h, 1); + leds_mask |= LED_SCR; break; /* XXX add other LEDs ? */ } } } hid_end_parse(d); if (report_id != NO_REPORT_ID) { data[2] = data[1]; data[1] = report_id; } if (found) write(s->intr, data, (report_id != NO_REPORT_ID) ? 3 : 2); + + if (found && s->srv->uinput && hid_device->keyboard) + uinput_rep_leds(s->ukbd, st.leds, leds_mask); return (0); } Index: head/usr.sbin/bluetooth/bthidd/parser.y =================================================================== --- head/usr.sbin/bluetooth/bthidd/parser.y (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/parser.y (revision 333113) @@ -1,532 +1,566 @@ %{ /* * parser.y */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #ifndef BTHIDCONTROL #include #include #define SYSLOG syslog #define LOGCRIT LOG_CRIT #define LOGERR LOG_ERR #define LOGWARNING LOG_WARNING #define EOL #else #define SYSLOG fprintf #define LOGCRIT stderr #define LOGERR stderr #define LOGWARNING stderr #define EOL "\n" #endif /* ndef BTHIDCONTROL */ #define NAMELESS_DEVICE "No Name" #include "bthid_config.h" int yylex (void); void yyerror (char const *); static int32_t check_hid_device(hid_device_p hid_device); static void free_hid_device (hid_device_p hid_device); extern FILE *yyin; extern int yylineno; char const *config_file = BTHIDD_CONFFILE; char const *hids_file = BTHIDD_HIDSFILE; static char buffer[1024]; static int32_t hid_descriptor_size; static hid_device_t *hid_device = NULL; static LIST_HEAD(, hid_device) hid_devices; %} %union { bdaddr_t bdaddr; int32_t num; char *string; } %token T_BDADDRSTRING %token T_HEXBYTE %token T_HEXWORD %token T_STRING %token T_NAME %token T_DEVICE T_BDADDR T_VENDOR_ID T_PRODUCT_ID T_VERSION T_CONTROL_PSM %token T_INTERRUPT_PSM T_RECONNECT_INITIATE T_BATTERY_POWER %token T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR %token T_TRUE T_FALSE T_ERROR %% config: line | config line ; line: T_DEVICE { hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device)); if (hid_device == NULL) { SYSLOG(LOGCRIT, "Could not allocate new " \ "config entry" EOL); YYABORT; } hid_device->new_device = 1; } '{' options '}' { if (check_hid_device(hid_device)) LIST_INSERT_HEAD(&hid_devices,hid_device,next); else free_hid_device(hid_device); hid_device = NULL; } ; options: option ';' | options option ';' ; option: bdaddr | name | vendor_id | product_id | version | control_psm | interrupt_psm | reconnect_initiate | battery_power | normally_connectable | hid_descriptor | parser_error ; bdaddr: T_BDADDR T_BDADDRSTRING { memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); } ; name: T_NAME T_STRING { if (hid_device->name != NULL) { free(hid_device->name); hid_device->name = NULL; } if (strcmp($2, NAMELESS_DEVICE)) { hid_device->name = strdup($2); if (hid_device->name == NULL) { SYSLOG(LOGCRIT, "Could not allocate new " \ "device name" EOL); YYABORT; } } } ; vendor_id: T_VENDOR_ID T_HEXWORD { hid_device->vendor_id = $2; } ; product_id: T_PRODUCT_ID T_HEXWORD { hid_device->product_id = $2; } ; version: T_VERSION T_HEXWORD { hid_device->version = $2; } ; control_psm: T_CONTROL_PSM T_HEXBYTE { hid_device->control_psm = $2; } ; interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE { hid_device->interrupt_psm = $2; } ; reconnect_initiate: T_RECONNECT_INITIATE T_TRUE { hid_device->reconnect_initiate = 1; } | T_RECONNECT_INITIATE T_FALSE { hid_device->reconnect_initiate = 0; } ; battery_power: T_BATTERY_POWER T_TRUE { hid_device->battery_power = 1; } | T_BATTERY_POWER T_FALSE { hid_device->battery_power = 0; } ; normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE { hid_device->normally_connectable = 1; } | T_NORMALLY_CONNECTABLE T_FALSE { hid_device->normally_connectable = 0; } ; hid_descriptor: T_HID_DESCRIPTOR { hid_descriptor_size = 0; } '{' hid_descriptor_bytes '}' { if (hid_device->desc != NULL) hid_dispose_report_desc(hid_device->desc); hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size); if (hid_device->desc == NULL) { SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL); YYABORT; } } ; hid_descriptor_bytes: hid_descriptor_byte | hid_descriptor_bytes hid_descriptor_byte ; hid_descriptor_byte: T_HEXBYTE { if (hid_descriptor_size >= (int32_t) sizeof(buffer)) { SYSLOG(LOGCRIT, "HID descriptor is too big" EOL); YYABORT; } buffer[hid_descriptor_size ++] = $1; } ; parser_error: T_ERROR { YYABORT; } %% /* Display parser error message */ void yyerror(char const *message) { SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); } /* Re-read config file */ int32_t read_config_file(void) { int32_t e; if (config_file == NULL) { SYSLOG(LOGERR, "Unknown config file name!" EOL); return (-1); } if ((yyin = fopen(config_file, "r")) == NULL) { SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL, config_file, strerror(errno), errno); return (-1); } clean_config(); if (yyparse() < 0) { SYSLOG(LOGERR, "Could not parse config file '%s'" EOL, config_file); e = -1; } else e = 0; fclose(yyin); yyin = NULL; return (e); } /* Clean config */ void clean_config(void) { while (!LIST_EMPTY(&hid_devices)) { hid_device_p d = LIST_FIRST(&hid_devices); LIST_REMOVE(d, next); free_hid_device(d); } } /* Lookup config entry */ hid_device_p get_hid_device(bdaddr_p bdaddr) { hid_device_p d; LIST_FOREACH(d, &hid_devices, next) if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) break; return (d); } /* Get next config entry */ hid_device_p get_next_hid_device(hid_device_p d) { return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next)); } /* Print config entry */ void print_hid_device(hid_device_p d, FILE *f) { /* XXX FIXME hack! */ struct report_desc { unsigned int size; unsigned char data[1]; }; /* XXX FIXME hack! */ struct report_desc *desc = (struct report_desc *) d->desc; uint32_t i; fprintf(f, "device {\n" \ " bdaddr %s;\n" \ " name \"%s\";\n" \ " vendor_id 0x%04x;\n" \ " product_id 0x%04x;\n" \ " version 0x%04x;\n" \ " control_psm 0x%x;\n" \ " interrupt_psm 0x%x;\n" \ " reconnect_initiate %s;\n" \ " battery_power %s;\n" \ " normally_connectable %s;\n" \ " hid_descriptor {", bt_ntoa(&d->bdaddr, NULL), (d->name != NULL)? d->name : NAMELESS_DEVICE, d->vendor_id, d->product_id, d->version, d->control_psm, d->interrupt_psm, d->reconnect_initiate? "true" : "false", d->battery_power? "true" : "false", d->normally_connectable? "true" : "false"); for (i = 0; i < desc->size; i ++) { if ((i % 8) == 0) fprintf(f, "\n "); fprintf(f, "0x%2.2x ", desc->data[i]); } fprintf(f, "\n" \ " };\n" \ "}\n"); } /* Check config entry */ static int32_t check_hid_device(hid_device_p d) { hid_data_t hd; hid_item_t hi; - int32_t page; + int32_t page, mdepth; if (get_hid_device(&d->bdaddr) != NULL) { SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL, bt_ntoa(&d->bdaddr, NULL)); return (0); } if (d->control_psm == 0) { SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL); return (0); } if (d->interrupt_psm == 0) { SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL); return (0); } if (d->desc == NULL) { SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL); return (0); } + mdepth = 0; + /* XXX somehow need to make sure descriptor is valid */ for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) { switch (hi.kind) { case hid_collection: + if (mdepth != 0) + mdepth++; + else if (hi.collection == 1 && + hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) + mdepth++; + break; case hid_endcollection: + if (mdepth != 0) + mdepth--; + break; case hid_output: case hid_feature: break; case hid_input: /* Check if the device may send keystrokes */ page = HID_PAGE(hi.usage); if (page == HUP_KEYBOARD) d->keyboard = 1; + if (page == HUP_CONSUMER && + (hi.flags & (HIO_CONST|HIO_RELATIVE)) == 0) + d->has_cons = 1; + /* Check if the device may send relative motion events */ + if (mdepth == 0) + break; + if (hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && + (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) + d->mouse = 1; + if (hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && + (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) + d->mouse = 1; + if (hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL) && + (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) + d->has_wheel = 1; + if (hi.usage == + HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN) && + (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) + d->has_hwheel = 1; break; } } hid_end_parse(hd); return (1); } /* Free config entry */ static void free_hid_device(hid_device_p d) { if (d->desc != NULL) hid_dispose_report_desc(d->desc); free(d->name); memset(d, 0, sizeof(*d)); free(d); } /* Re-read hids file */ int32_t read_hids_file(void) { FILE *f; hid_device_t *d; char *line; bdaddr_t bdaddr; int32_t lineno; if (hids_file == NULL) { SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); return (-1); } if ((f = fopen(hids_file, "r")) == NULL) { if (errno == ENOENT) return (0); SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, hids_file, strerror(errno), errno); return (-1); } for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) { if ((line = strtok(buffer, "\r\n\t ")) == NULL) continue; /* ignore empty lines */ if (!bt_aton(line, &bdaddr)) { SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \ "%s:%d" EOL, hids_file, lineno); continue; } if ((d = get_hid_device(&bdaddr)) != NULL) d->new_device = 0; } fclose(f); return (0); } /* Write hids file */ int32_t write_hids_file(void) { char path[PATH_MAX]; FILE *f; hid_device_t *d; if (hids_file == NULL) { SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); return (-1); } snprintf(path, sizeof(path), "%s.new", hids_file); if ((f = fopen(path, "w")) == NULL) { SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, path, strerror(errno), errno); return (-1); } LIST_FOREACH(d, &hid_devices, next) if (!d->new_device) fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL)); fclose(f); if (rename(path, hids_file) < 0) { SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \ "%s (%d)" EOL, path, hids_file, strerror(errno), errno); unlink(path); return (-1); } return (0); } Index: head/usr.sbin/bluetooth/bthidd/server.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/server.c (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/server.c (revision 333113) @@ -1,358 +1,357 @@ /* * server.c */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: server.c,v 1.9 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #define L2CAP_SOCKET_CHECKED #include +#include #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" +#include "btuinput.h" #include "kbd.h" #undef max #define max(x, y) (((x) > (y))? (x) : (y)) static int32_t server_accept (bthid_server_p srv, int32_t fd); static int32_t server_process(bthid_server_p srv, int32_t fd); /* * Initialize server */ int32_t server_init(bthid_server_p srv) { struct sockaddr_l2cap l2addr; assert(srv != NULL); srv->ctrl = srv->intr = -1; FD_ZERO(&srv->rfdset); FD_ZERO(&srv->wfdset); LIST_INIT(&srv->sessions); /* Open /dev/consolectl */ srv->cons = open("/dev/consolectl", O_RDWR); if (srv->cons < 0) { syslog(LOG_ERR, "Could not open /dev/consolectl. %s (%d)", strerror(errno), errno); return (-1); } /* Create control socket */ srv->ctrl = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (srv->ctrl < 0) { syslog(LOG_ERR, "Could not create control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->cons); return (-1); } l2addr.l2cap_len = sizeof(l2addr); l2addr.l2cap_family = AF_BLUETOOTH; memcpy(&l2addr.l2cap_bdaddr, &srv->bdaddr, sizeof(l2addr.l2cap_bdaddr)); l2addr.l2cap_psm = htole16(0x11); l2addr.l2cap_bdaddr_type = BDADDR_BREDR; l2addr.l2cap_cid = 0; if (bind(srv->ctrl, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { syslog(LOG_ERR, "Could not bind control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->ctrl); close(srv->cons); return (-1); } if (listen(srv->ctrl, 10) < 0) { syslog(LOG_ERR, "Could not listen on control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->ctrl); close(srv->cons); return (-1); } /* Create intrrupt socket */ srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (srv->intr < 0) { syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->ctrl); close(srv->cons); return (-1); } l2addr.l2cap_psm = htole16(0x13); if (bind(srv->intr, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { syslog(LOG_ERR, "Could not bind interrupt L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->intr); close(srv->ctrl); close(srv->cons); return (-1); } if (listen(srv->intr, 10) < 0) { syslog(LOG_ERR, "Could not listen on interrupt L2CAP socket. "\ "%s (%d)", strerror(errno), errno); close(srv->intr); close(srv->ctrl); close(srv->cons); return (-1); } FD_SET(srv->ctrl, &srv->rfdset); FD_SET(srv->intr, &srv->rfdset); srv->maxfd = max(srv->ctrl, srv->intr); return (0); } /* * Shutdown server */ void server_shutdown(bthid_server_p srv) { assert(srv != NULL); close(srv->cons); close(srv->ctrl); close(srv->intr); while (!LIST_EMPTY(&srv->sessions)) session_close(LIST_FIRST(&srv->sessions)); memset(srv, 0, sizeof(*srv)); } /* * Do one server iteration */ int32_t server_do(bthid_server_p srv) { struct timeval tv; fd_set rfdset, wfdset; int32_t n, fd; assert(srv != NULL); tv.tv_sec = 1; tv.tv_usec = 0; /* Copy cached version of the fd sets and call select */ memcpy(&rfdset, &srv->rfdset, sizeof(rfdset)); memcpy(&wfdset, &srv->wfdset, sizeof(wfdset)); n = select(srv->maxfd + 1, &rfdset, &wfdset, NULL, &tv); if (n < 0) { if (errno == EINTR) return (0); syslog(LOG_ERR, "Could not select(%d, %p, %p). %s (%d)", srv->maxfd + 1, &rfdset, &wfdset, strerror(errno), errno); return (-1); } /* Process descriptors (if any) */ for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) { if (FD_ISSET(fd, &rfdset)) { n --; if (fd == srv->ctrl || fd == srv->intr) server_accept(srv, fd); else server_process(srv, fd); } else if (FD_ISSET(fd, &wfdset)) { n --; client_connect(srv, fd); } } return (0); } /* * Accept new connection */ static int32_t server_accept(bthid_server_p srv, int32_t fd) { bthid_session_p s; hid_device_p d; struct sockaddr_l2cap l2addr; int32_t new_fd; socklen_t len; len = sizeof(l2addr); if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) { syslog(LOG_ERR, "Could not accept %s connection. %s (%d)", (fd == srv->ctrl)? "control" : "interrupt", strerror(errno), errno); return (-1); } /* Is device configured? */ if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) { syslog(LOG_ERR, "Rejecting %s connection from %s. " \ "Device not configured", (fd == srv->ctrl)? "control" : "interrupt", bt_ntoa(&l2addr.l2cap_bdaddr, NULL)); close(new_fd); return (-1); } /* Check if we have session for the device */ if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) { d->new_device = 0; /* reset new device flag */ write_hids_file(); /* Create new inbound session */ if ((s = session_open(srv, d)) == NULL) { syslog(LOG_CRIT, "Could not open inbound session " "for %s", bt_ntoa(&l2addr.l2cap_bdaddr, NULL)); close(new_fd); return (-1); } } /* Update descriptors */ if (fd == srv->ctrl) { assert(s->ctrl == -1); s->ctrl = new_fd; s->state = (s->intr == -1)? W4INTR : OPEN; } else { assert(s->intr == -1); s->intr = new_fd; s->state = (s->ctrl == -1)? W4CTRL : OPEN; } FD_SET(new_fd, &srv->rfdset); if (new_fd > srv->maxfd) srv->maxfd = new_fd; syslog(LOG_NOTICE, "Accepted %s connection from %s", (fd == srv->ctrl)? "control" : "interrupt", bt_ntoa(&l2addr.l2cap_bdaddr, NULL)); - /* Register session's vkbd descriptor (if needed) for read */ - if (s->state == OPEN && d->keyboard) { - assert(s->vkbd != -1); - - FD_SET(s->vkbd, &srv->rfdset); - if (s->vkbd > srv->maxfd) - srv->maxfd = s->vkbd; + /* Create virtual kbd/mouse after both channels are established */ + if (s->state == OPEN && session_run(s) < 0) { + session_close(s); + return (-1); } - /* Pass device for probing after both channels are established */ - if (s->state == OPEN) - hid_initialise(s); - return (0); } /* * Process data on the connection */ static int32_t server_process(bthid_server_p srv, int32_t fd) { bthid_session_p s = session_by_fd(srv, fd); int32_t len, to_read; int32_t (*cb)(bthid_session_p, uint8_t *, int32_t); union { - uint8_t b[1024]; - vkbd_status_t s; - } data; + uint8_t b[1024]; + vkbd_status_t s; + struct input_event ie; + } data; if (s == NULL) return (0); /* can happen on device disconnect */ if (fd == s->ctrl) { cb = hid_control; to_read = sizeof(data.b); } else if (fd == s->intr) { cb = hid_interrupt; to_read = sizeof(data.b); + } else if (fd == s->ukbd) { + cb = uinput_kbd_status_changed; + to_read = sizeof(data.ie); } else { assert(fd == s->vkbd); cb = kbd_status_changed; to_read = sizeof(data.s); } do { len = read(fd, &data, to_read); } while (len < 0 && errno == EINTR); if (len < 0) { syslog(LOG_ERR, "Could not read data from %s (%s). %s (%d)", bt_ntoa(&s->bdaddr, NULL), (fd == s->ctrl)? "control" : "interrupt", strerror(errno), errno); session_close(s); return (0); } if (len == 0) { syslog(LOG_NOTICE, "Remote device %s has closed %s connection", bt_ntoa(&s->bdaddr, NULL), (fd == s->ctrl)? "control" : "interrupt"); session_close(s); return (0); } (*cb)(s, (uint8_t *) &data, len); return (0); } Index: head/usr.sbin/bluetooth/bthidd/session.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/session.c (revision 333112) +++ head/usr.sbin/bluetooth/bthidd/session.c (revision 333113) @@ -1,189 +1,250 @@ /* * session.c */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: session.c,v 1.3 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" +#include "btuinput.h" #include "kbd.h" /* * Create new session */ bthid_session_p session_open(bthid_server_p srv, hid_device_p const d) { bthid_session_p s; assert(srv != NULL); assert(d != NULL); if ((s = (bthid_session_p) malloc(sizeof(*s))) == NULL) return (NULL); s->srv = srv; memcpy(&s->bdaddr, &d->bdaddr, sizeof(s->bdaddr)); s->ctrl = -1; s->intr = -1; + s->vkbd = -1; s->ctx = NULL; - - if (d->keyboard) { - /* Open /dev/vkbdctl */ - s->vkbd = open("/dev/vkbdctl", O_RDWR); - if (s->vkbd < 0) { - syslog(LOG_ERR, "Could not open /dev/vkbdctl " \ - "for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), - strerror(errno), errno); - free(s); - return (NULL); - } - } else - s->vkbd = -1; - s->state = CLOSED; + s->ukbd = -1; + s->umouse = -1; + s->obutt = 0; s->keys1 = bit_alloc(kbd_maxkey()); if (s->keys1 == NULL) { free(s); return (NULL); } s->keys2 = bit_alloc(kbd_maxkey()); if (s->keys2 == NULL) { free(s->keys1); free(s); return (NULL); } LIST_INSERT_HEAD(&srv->sessions, s, next); return (s); } /* + * Initialize virtual keyboard and mouse after both channels are established + */ + +int32_t +session_run(bthid_session_p s) +{ + hid_device_p d = get_hid_device(&s->bdaddr); + struct sockaddr_l2cap local; + socklen_t len; + + if (d->keyboard) { + /* Open /dev/vkbdctl */ + s->vkbd = open("/dev/vkbdctl", O_RDWR); + if (s->vkbd < 0) { + syslog(LOG_ERR, "Could not open /dev/vkbdctl " \ + "for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), + strerror(errno), errno); + return (-1); + } + /* Register session's vkbd descriptor (if needed) for read */ + FD_SET(s->vkbd, &s->srv->rfdset); + if (s->vkbd > s->srv->maxfd) + s->srv->maxfd = s->vkbd; + } + + /* Pass device for probing */ + hid_initialise(s); + + /* Take local bdaddr */ + len = sizeof(local); + getsockname(s->ctrl, (struct sockaddr *) &local, &len); + + if (d->mouse && s->srv->uinput) { + s->umouse = uinput_open_mouse(d, &local.l2cap_bdaddr); + if (s->umouse < 0) { + syslog(LOG_ERR, "Could not open /dev/uinput " \ + "for %s. %s (%d)", bt_ntoa(&s->bdaddr, + NULL), strerror(errno), errno); + return (-1); + } + } + if (d->keyboard && s->srv->uinput) { + s->ukbd = uinput_open_keyboard(d, &local.l2cap_bdaddr); + if (s->ukbd < 0) { + syslog(LOG_ERR, "Could not open /dev/uinput " \ + "for %s. %s (%d)", bt_ntoa(&s->bdaddr, + NULL), strerror(errno), errno); + return (-1); + } + /* Register session's ukbd descriptor (if needed) for read */ + FD_SET(s->ukbd, &s->srv->rfdset); + if (s->ukbd > s->srv->maxfd) + s->srv->maxfd = s->ukbd; + } + return (0); +} + +/* * Lookup session by bdaddr */ bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr) { bthid_session_p s; assert(srv != NULL); assert(bdaddr != NULL); LIST_FOREACH(s, &srv->sessions, next) if (memcmp(&s->bdaddr, bdaddr, sizeof(s->bdaddr)) == 0) break; return (s); } /* * Lookup session by fd */ bthid_session_p session_by_fd(bthid_server_p srv, int32_t fd) { bthid_session_p s; assert(srv != NULL); assert(fd >= 0); LIST_FOREACH(s, &srv->sessions, next) - if (s->ctrl == fd || s->intr == fd || s->vkbd == fd) + if (s->ctrl == fd || s->intr == fd || + s->vkbd == fd || s->ukbd == fd) break; return (s); } /* * Close session */ void session_close(bthid_session_p s) { assert(s != NULL); assert(s->srv != NULL); LIST_REMOVE(s, next); if (s->intr != -1) { FD_CLR(s->intr, &s->srv->rfdset); FD_CLR(s->intr, &s->srv->wfdset); close(s->intr); if (s->srv->maxfd == s->intr) s->srv->maxfd --; } if (s->ctrl != -1) { FD_CLR(s->ctrl, &s->srv->rfdset); FD_CLR(s->ctrl, &s->srv->wfdset); close(s->ctrl); if (s->srv->maxfd == s->ctrl) s->srv->maxfd --; } if (s->vkbd != -1) { FD_CLR(s->vkbd, &s->srv->rfdset); close(s->vkbd); if (s->srv->maxfd == s->vkbd) + s->srv->maxfd --; + } + + if (s->umouse != -1) + close(s->umouse); + + if (s->ukbd != -1) { + FD_CLR(s->ukbd, &s->srv->rfdset); + close(s->ukbd); + + if (s->srv->maxfd == s->ukbd) s->srv->maxfd --; } free(s->ctx); free(s->keys1); free(s->keys2); memset(s, 0, sizeof(*s)); free(s); }