head 1.26; access; symbols netbsd-10-0-RC6:1.26 netbsd-10-0-RC5:1.26 netbsd-10-0-RC4:1.26 netbsd-10-0-RC3:1.26 netbsd-10-0-RC2:1.26 thorpej-ifq:1.26.0.8 thorpej-ifq-base:1.26 thorpej-altq-separation:1.26.0.6 thorpej-altq-separation-base:1.26 netbsd-10-0-RC1:1.26 netbsd-10:1.26.0.4 netbsd-10-base:1.26 bouyer-sunxi-drm:1.26.0.2 bouyer-sunxi-drm-base:1.26 netbsd-9-3-RELEASE:1.25 thorpej-i2c-spi-conf2:1.25.0.26 thorpej-i2c-spi-conf2-base:1.25 thorpej-futex2:1.25.0.24 thorpej-futex2-base:1.25 thorpej-cfargs2:1.25.0.22 thorpej-cfargs2-base:1.25 cjep_sun2x-base1:1.25 cjep_sun2x:1.25.0.20 cjep_sun2x-base:1.25 cjep_staticlib_x-base1:1.25 netbsd-9-2-RELEASE:1.25 cjep_staticlib_x:1.25.0.18 cjep_staticlib_x-base:1.25 thorpej-i2c-spi-conf:1.25.0.16 thorpej-i2c-spi-conf-base:1.25 thorpej-cfargs:1.25.0.14 thorpej-cfargs-base:1.25 thorpej-futex:1.25.0.12 thorpej-futex-base:1.25 netbsd-9-1-RELEASE:1.25 bouyer-xenpvh-base2:1.25 phil-wifi-20200421:1.25 bouyer-xenpvh-base1:1.25 phil-wifi-20200411:1.25 bouyer-xenpvh:1.25.0.10 bouyer-xenpvh-base:1.25 is-mlppp:1.25.0.8 is-mlppp-base:1.25 phil-wifi-20200406:1.25 netbsd-8-2-RELEASE:1.24 ad-namecache-base3:1.25 netbsd-9-0-RELEASE:1.25 netbsd-9-0-RC2:1.25 ad-namecache-base2:1.25 ad-namecache-base1:1.25 ad-namecache:1.25.0.6 ad-namecache-base:1.25 netbsd-9-0-RC1:1.25 phil-wifi-20191119:1.25 netbsd-9:1.25.0.4 netbsd-9-base:1.25 phil-wifi-20190609:1.25 netbsd-8-1-RELEASE:1.24 netbsd-8-1-RC1:1.24 isaki-audio2:1.25.0.2 isaki-audio2-base:1.25 pgoyette-compat-merge-20190127:1.24.26.1 pgoyette-compat-20190127:1.25 pgoyette-compat-20190118:1.25 pgoyette-compat-1226:1.25 pgoyette-compat-1126:1.25 pgoyette-compat-1020:1.25 pgoyette-compat-0930:1.25 pgoyette-compat-0906:1.24 netbsd-7-2-RELEASE:1.24 pgoyette-compat-0728:1.24 netbsd-8-0-RELEASE:1.24 phil-wifi:1.24.0.28 phil-wifi-base:1.24 pgoyette-compat-0625:1.24 netbsd-8-0-RC2:1.24 pgoyette-compat-0521:1.24 pgoyette-compat-0502:1.24 pgoyette-compat-0422:1.24 netbsd-8-0-RC1:1.24 pgoyette-compat-0415:1.24 pgoyette-compat-0407:1.24 pgoyette-compat-0330:1.24 pgoyette-compat-0322:1.24 pgoyette-compat-0315:1.24 netbsd-7-1-2-RELEASE:1.24 pgoyette-compat:1.24.0.26 pgoyette-compat-base:1.24 netbsd-7-1-1-RELEASE:1.24 tls-maxphys-base-20171202:1.24 matt-nb8-mediatek:1.24.0.24 matt-nb8-mediatek-base:1.24 nick-nhusb-base-20170825:1.24 perseant-stdc-iso10646:1.24.0.22 perseant-stdc-iso10646-base:1.24 netbsd-8:1.24.0.20 netbsd-8-base:1.24 prg-localcount2-base3:1.24 prg-localcount2-base2:1.24 prg-localcount2-base1:1.24 prg-localcount2:1.24.0.18 prg-localcount2-base:1.24 pgoyette-localcount-20170426:1.24 bouyer-socketcan-base1:1.24 jdolecek-ncq:1.24.0.16 jdolecek-ncq-base:1.24 pgoyette-localcount-20170320:1.24 netbsd-7-1:1.24.0.14 netbsd-7-1-RELEASE:1.24 netbsd-7-1-RC2:1.24 nick-nhusb-base-20170204:1.24 netbsd-7-nhusb-base-20170116:1.24 bouyer-socketcan:1.24.0.12 bouyer-socketcan-base:1.24 pgoyette-localcount-20170107:1.24 netbsd-7-1-RC1:1.24 nick-nhusb-base-20161204:1.24 pgoyette-localcount-20161104:1.24 netbsd-7-0-2-RELEASE:1.24 nick-nhusb-base-20161004:1.24 localcount-20160914:1.24 netbsd-7-nhusb:1.24.0.10 netbsd-7-nhusb-base:1.24 pgoyette-localcount-20160806:1.24 pgoyette-localcount-20160726:1.24 pgoyette-localcount:1.24.0.8 pgoyette-localcount-base:1.24 nick-nhusb-base-20160907:1.24 nick-nhusb-base-20160529:1.24 netbsd-7-0-1-RELEASE:1.24 nick-nhusb-base-20160422:1.24 nick-nhusb-base-20160319:1.24 nick-nhusb-base-20151226:1.24 netbsd-7-0:1.24.0.6 netbsd-7-0-RELEASE:1.24 nick-nhusb-base-20150921:1.24 netbsd-7-0-RC3:1.24 netbsd-7-0-RC2:1.24 netbsd-7-0-RC1:1.24 nick-nhusb-base-20150606:1.24 nick-nhusb-base-20150406:1.24 nick-nhusb:1.24.0.4 nick-nhusb-base:1.24 netbsd-5-2-3-RELEASE:1.20.12.1 netbsd-5-1-5-RELEASE:1.20 netbsd-6-0-6-RELEASE:1.23 netbsd-6-1-5-RELEASE:1.23 netbsd-7:1.24.0.2 netbsd-7-base:1.24 yamt-pagecache-base9:1.23 yamt-pagecache-tag8:1.23 netbsd-6-1-4-RELEASE:1.23 netbsd-6-0-5-RELEASE:1.23 tls-earlyentropy:1.23.0.26 tls-earlyentropy-base:1.24 riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.23 riastradh-drm2-base3:1.23 netbsd-6-1-3-RELEASE:1.23 netbsd-6-0-4-RELEASE:1.23 netbsd-5-2-2-RELEASE:1.20.12.1 netbsd-5-1-4-RELEASE:1.20 netbsd-6-1-2-RELEASE:1.23 netbsd-6-0-3-RELEASE:1.23 netbsd-5-2-1-RELEASE:1.20.12.1 netbsd-5-1-3-RELEASE:1.20 rmind-smpnet-nbase:1.23 netbsd-6-1-1-RELEASE:1.23 riastradh-drm2-base2:1.23 riastradh-drm2-base1:1.23 riastradh-drm2:1.23.0.24 riastradh-drm2-base:1.23 rmind-smpnet:1.23.0.16 rmind-smpnet-base:1.23 netbsd-6-1:1.23.0.22 netbsd-6-0-2-RELEASE:1.23 netbsd-6-1-RELEASE:1.23 khorben-n900:1.23.0.20 netbsd-6-1-RC4:1.23 netbsd-6-1-RC3:1.23 agc-symver:1.23.0.18 agc-symver-base:1.23 netbsd-6-1-RC2:1.23 netbsd-6-1-RC1:1.23 yamt-pagecache-base8:1.23 netbsd-5-2:1.20.12.1.0.4 netbsd-6-0-1-RELEASE:1.23 yamt-pagecache-base7:1.23 netbsd-5-2-RELEASE:1.20.12.1 netbsd-5-2-RC1:1.20.12.1 matt-nb6-plus-nbase:1.23 yamt-pagecache-base6:1.23 netbsd-6-0:1.23.0.14 netbsd-6-0-RELEASE:1.23 netbsd-6-0-RC2:1.23 tls-maxphys:1.23.0.12 tls-maxphys-base:1.24 matt-nb6-plus:1.23.0.10 matt-nb6-plus-base:1.23 netbsd-6-0-RC1:1.23 jmcneill-usbmp-base10:1.23 yamt-pagecache-base5:1.23 jmcneill-usbmp-base9:1.23 yamt-pagecache-base4:1.23 jmcneill-usbmp-base8:1.23 jmcneill-usbmp-base7:1.23 jmcneill-usbmp-base6:1.23 jmcneill-usbmp-base5:1.23 jmcneill-usbmp-base4:1.23 jmcneill-usbmp-base3:1.23 jmcneill-usbmp-pre-base2:1.23 jmcneill-usbmp-base2:1.23 netbsd-6:1.23.0.8 netbsd-6-base:1.23 netbsd-5-1-2-RELEASE:1.20 netbsd-5-1-1-RELEASE:1.20 jmcneill-usbmp:1.23.0.6 jmcneill-usbmp-base:1.23 jmcneill-audiomp3:1.23.0.4 jmcneill-audiomp3-base:1.23 yamt-pagecache-base3:1.23 yamt-pagecache-base2:1.23 yamt-pagecache:1.23.0.2 yamt-pagecache-base:1.23 rmind-uvmplock-nbase:1.22 cherry-xenmp:1.22.0.6 cherry-xenmp-base:1.22 bouyer-quota2-nbase:1.22 bouyer-quota2:1.22.0.4 bouyer-quota2-base:1.22 jruoho-x86intr:1.22.0.2 jruoho-x86intr-base:1.22 matt-mips64-premerge-20101231:1.22 matt-nb5-mips64-premerge-20101231:1.20 matt-nb5-pq3:1.20.12.1.0.2 matt-nb5-pq3-base:1.20.12.1 netbsd-5-1:1.20.0.22 netbsd-5-1-RELEASE:1.20 uebayasi-xip-base4:1.22 uebayasi-xip-base3:1.22 yamt-nfs-mp-base11:1.21 netbsd-5-1-RC4:1.20 matt-nb5-mips64-k15:1.20 uebayasi-xip-base2:1.21 yamt-nfs-mp-base10:1.21 netbsd-5-1-RC3:1.20 netbsd-5-1-RC2:1.20 uebayasi-xip-base1:1.21 netbsd-5-1-RC1:1.20 rmind-uvmplock:1.21.0.4 rmind-uvmplock-base:1.22 yamt-nfs-mp-base9:1.21 uebayasi-xip:1.21.0.2 uebayasi-xip-base:1.21 netbsd-5-0-2-RELEASE:1.20 matt-nb5-mips64-premerge-20091211:1.20 matt-premerge-20091211:1.21 yamt-nfs-mp-base8:1.20 matt-nb5-mips64-u2-k2-k4-k7-k8-k9:1.20 matt-nb4-mips64-k7-u2a-k9b:1.20 matt-nb5-mips64-u1-k1-k5:1.20 yamt-nfs-mp-base7:1.20 matt-nb5-mips64:1.20.0.20 netbsd-5-0-1-RELEASE:1.20 jymxensuspend-base:1.20 yamt-nfs-mp-base6:1.20 yamt-nfs-mp-base5:1.20 yamt-nfs-mp-base4:1.20 jym-xensuspend-nbase:1.21 yamt-nfs-mp-base3:1.20 nick-hppapmap-base4:1.20 nick-hppapmap-base3:1.20 netbsd-5-0:1.20.0.18 netbsd-5-0-RELEASE:1.20 netbsd-5-0-RC4:1.20 netbsd-5-0-RC3:1.20 nick-hppapmap-base2:1.20 netbsd-5-0-RC2:1.20 jym-xensuspend:1.20.0.16 jym-xensuspend-base:1.20 netbsd-5-0-RC1:1.20 haad-dm-base2:1.20 haad-nbase2:1.20 ad-audiomp2:1.20.0.14 ad-audiomp2-base:1.20 netbsd-5:1.20.0.12 netbsd-5-base:1.20 nick-hppapmap:1.20.0.10 nick-hppapmap-base:1.20 matt-mips64-base2:1.20 matt-mips64:1.12.0.10 haad-dm-base1:1.20 wrstuden-revivesa-base-4:1.20 netbsd-4-0-1-RELEASE:1.8.2.1 wrstuden-revivesa-base-3:1.20 wrstuden-revivesa-base-2:1.20 wrstuden-fixsa-newbase:1.8.2.1 nick-csl-alignment-base5:1.13 haad-dm:1.20.0.8 haad-dm-base:1.20 wrstuden-revivesa-base-1:1.20 simonb-wapbl-nbase:1.20 yamt-pf42-base4:1.20 simonb-wapbl:1.20.0.6 simonb-wapbl-base:1.20 yamt-pf42-base3:1.20 hpcarm-cleanup-nbase:1.20 yamt-pf42-baseX:1.19 yamt-pf42-base2:1.20 yamt-nfs-mp-base2:1.20 wrstuden-revivesa:1.20.0.4 wrstuden-revivesa-base:1.20 yamt-nfs-mp:1.20.0.2 yamt-nfs-mp-base:1.20 yamt-pf42:1.19.0.2 yamt-pf42-base:1.19 ad-socklock-base1:1.19 yamt-lazymbuf-base15:1.19 yamt-lazymbuf-base14:1.19 keiichi-mipv6-nbase:1.19 mjf-devfs2:1.16.0.14 mjf-devfs2-base:1.20 nick-net80211-sync:1.16.0.12 nick-net80211-sync-base:1.16 keiichi-mipv6:1.16.0.10 keiichi-mipv6-base:1.19 bouyer-xeni386-merge1:1.16 matt-armv6-prevmlocking:1.12.8.1 wrstuden-fixsa-base-1:1.8.2.1 vmlocking2-base3:1.16 netbsd-4-0:1.8.2.1.0.4 netbsd-4-0-RELEASE:1.8.2.1 bouyer-xeni386-nbase:1.16 yamt-kmem-base3:1.16 cube-autoconf:1.16.0.8 cube-autoconf-base:1.16 yamt-kmem-base2:1.16 bouyer-xeni386:1.16.0.6 bouyer-xeni386-base:1.16 yamt-kmem:1.16.0.4 yamt-kmem-base:1.16 vmlocking2-base2:1.16 reinoud-bufcleanup-nbase:1.16 vmlocking2:1.16.0.2 vmlocking2-base1:1.16 netbsd-4-0-RC5:1.8.2.1 matt-nb4-arm:1.8.2.1.0.2 matt-nb4-arm-base:1.8.2.1 matt-armv6-nbase:1.19 jmcneill-base:1.15 netbsd-4-0-RC4:1.8.2.1 mjf-devfs:1.15.0.2 mjf-devfs-base:1.16 bouyer-xenamd64-base2:1.16 vmlocking-nbase:1.16 yamt-x86pmap-base4:1.14 bouyer-xenamd64:1.14.0.4 bouyer-xenamd64-base:1.16 netbsd-4-0-RC3:1.8.2.1 yamt-x86pmap-base3:1.14 yamt-x86pmap-base2:1.14 netbsd-4-0-RC2:1.8.2.1 yamt-x86pmap:1.14.0.2 yamt-x86pmap-base:1.14 netbsd-4-0-RC1:1.8.2.1 matt-armv6:1.12.0.8 matt-armv6-base:1.16 matt-mips64-base:1.12 jmcneill-pm:1.12.0.6 jmcneill-pm-base:1.16 hpcarm-cleanup:1.12.0.4 hpcarm-cleanup-base:1.16 nick-csl-alignment:1.12.0.2 nick-csl-alignment-base:1.12 yamt-idlelwp-base8:1.11 wrstuden-fixsa:1.8.0.4 wrstuden-fixsa-base:1.8.2.1 thorpej-atomic:1.10.0.2 thorpej-atomic-base:1.10 reinoud-bufcleanup:1.9.0.10 reinoud-bufcleanup-base:1.16 mjf-ufs-trans:1.9.0.8 mjf-ufs-trans-base:1.12 vmlocking:1.9.0.6 vmlocking-base:1.14 ad-audiomp:1.9.0.4 ad-audiomp-base:1.9 yamt-idlelwp:1.9.0.2 post-newlock2-merge:1.9 newlock2-nbase:1.9 yamt-splraiseipl-base5:1.8 yamt-splraiseipl-base4:1.8 yamt-splraiseipl-base3:1.8 abandoned-netbsd-4-base:1.3 abandoned-netbsd-4:1.3.0.2 yamt-splraiseipl-base2:1.7 yamt-splraiseipl:1.5.0.2 yamt-splraiseipl-base:1.5 yamt-pdpolicy-base9:1.5 rpaulo-netinet-merge-pcb-base:1.3 rpaulo-netinet-merge-pcb:1.3.0.6 newlock2:1.3.0.4 newlock2-base:1.9 yamt-pdpolicy-base8:1.3 yamt-pdpolicy-base7:1.3 netbsd-4:1.8.0.2 netbsd-4-base:1.8 gdamore-uart:1.1.0.8 yamt-pdpolicy:1.1.0.6 yamt-pdpolicy-base6:1.1 chap-midi-base:1.1 chap-midi:1.1.0.4 yamt-lazymbuf:1.1.0.2 chap-midi-nbase:1.1; locks; strict; comment @ * @; 1.26 date 2021.12.04.13.23.04; author andvar; state Exp; branches; next 1.25; commitid Zw4WYkXBhsrNPkjD; 1.25 date 2018.09.07.14.47.15; author plunky; state Exp; branches; next 1.24; commitid Ts6Cth1YQLxvhbRA; 1.24 date 2014.05.20.18.25.54; author rmind; state Exp; branches 1.24.26.1 1.24.28.1; next 1.23; commitid iUG1PXVDoOtq4jBx; 1.23 date 2011.07.27.10.25.09; author plunky; state Exp; branches 1.23.12.1 1.23.26.1; next 1.22; 1.22 date 2010.10.14.07.05.03; author plunky; state Exp; branches; next 1.21; 1.21 date 2009.09.24.19.35.09; author plunky; state Exp; branches 1.21.2.1 1.21.4.1; next 1.20; 1.20 date 2008.04.24.11.38.37; author ad; state Exp; branches 1.20.2.1 1.20.12.1; next 1.19; 1.19 date 2008.03.16.23.28.10; author plunky; state Exp; branches 1.19.2.1; next 1.18; 1.18 date 2008.03.16.23.14.24; author plunky; state Exp; branches; next 1.17; 1.17 date 2008.03.06.20.56.26; author plunky; state Exp; branches; next 1.16; 1.16 date 2007.11.10.23.12.22; author plunky; state Exp; branches 1.16.10.1 1.16.14.1; next 1.15; 1.15 date 2007.11.03.17.20.17; author plunky; state Exp; branches 1.15.2.1; next 1.14; 1.14 date 2007.09.16.19.59.30; author plunky; state Exp; branches 1.14.4.1; next 1.13; 1.13 date 2007.09.07.18.37.31; author plunky; state Exp; branches; next 1.12; 1.12 date 2007.07.09.21.11.10; author ad; state Exp; branches 1.12.2.1 1.12.6.1 1.12.8.1; next 1.11; 1.11 date 2007.04.21.06.15.23; author plunky; state Exp; branches; next 1.10; 1.10 date 2007.03.30.20.47.02; author plunky; state Exp; branches; next 1.9; 1.9 date 2006.12.26.00.00.22; author alc; state Exp; branches 1.9.2.1 1.9.6.1 1.9.8.1; next 1.8; 1.8 date 2006.11.16.01.33.45; author christos; state Exp; branches 1.8.2.1 1.8.4.1; next 1.7; 1.7 date 2006.10.12.01.32.37; author christos; state Exp; branches; next 1.6; 1.6 date 2006.10.04.15.49.59; author christos; state Exp; branches; next 1.5; 1.5 date 2006.09.11.22.12.39; author plunky; state Exp; branches 1.5.2.1; next 1.4; 1.4 date 2006.09.11.22.08.38; author plunky; state Exp; branches; next 1.3; 1.3 date 2006.07.26.10.20.56; author tron; state Exp; branches 1.3.2.1 1.3.4.1 1.3.6.1; next 1.2; 1.2 date 2006.07.26.10.10.06; author tron; state Exp; branches; next 1.1; 1.1 date 2006.06.19.15.44.45; author gdamore; state Exp; branches 1.1.2.1 1.1.4.1 1.1.6.1 1.1.8.1; next ; 1.24.26.1 date 2018.09.30.01.45.56; author pgoyette; state Exp; branches; next ; commitid SQ44grEPCeKPh4UA; 1.24.28.1 date 2019.06.10.22.09.46; author christos; state Exp; branches; next ; commitid jtc8rnCzWiEEHGqB; 1.23.12.1 date 2014.08.20.00.04.35; author tls; state Exp; branches; next ; commitid jTnpym9Qu0o4R1Nx; 1.23.26.1 date 2014.08.10.06.56.23; author tls; state Exp; branches; next ; commitid Q3VWPxpfW0ywCMLx; 1.21.2.1 date 2010.10.22.07.22.39; author uebayasi; state Exp; branches; next ; 1.21.4.1 date 2011.03.05.20.55.56; author rmind; state Exp; branches; next ; 1.20.2.1 date 2010.03.11.15.04.28; author yamt; state Exp; branches; next ; 1.20.12.1 date 2010.11.21.21.36.07; author riz; state Exp; branches; next ; 1.19.2.1 date 2008.05.18.12.35.28; author yamt; state Exp; branches; next ; 1.16.10.1 date 2008.03.24.07.16.24; author keiichi; state Exp; branches; next ; 1.16.14.1 date 2008.04.03.12.43.08; author mjf; state Exp; branches; next 1.16.14.2; 1.16.14.2 date 2008.06.02.13.24.23; author mjf; state Exp; branches; next ; 1.15.2.1 date 2007.11.19.00.49.07; author mjf; state Exp; branches; next ; 1.14.4.1 date 2007.11.13.16.02.47; author bouyer; state Exp; branches; next ; 1.12.2.1 date 2007.09.10.10.56.14; author skrll; state Exp; branches; next ; 1.12.6.1 date 2007.10.02.18.29.17; author joerg; state Exp; branches; next 1.12.6.2; 1.12.6.2 date 2007.11.04.21.03.36; author jmcneill; state Exp; branches; next 1.12.6.3; 1.12.6.3 date 2007.11.11.16.48.27; author joerg; state Exp; branches; next ; 1.12.8.1 date 2007.11.06.23.33.42; author matt; state Exp; branches; next 1.12.8.2; 1.12.8.2 date 2008.01.09.01.57.22; author matt; state Exp; branches; next 1.12.8.3; 1.12.8.3 date 2008.03.23.02.05.06; author matt; state Exp; branches; next ; 1.9.2.1 date 2007.04.15.16.03.58; author yamt; state Exp; branches; next 1.9.2.2; 1.9.2.2 date 2007.05.07.10.55.56; author yamt; state Exp; branches; next ; 1.9.6.1 date 2007.04.10.13.26.48; author ad; state Exp; branches; next 1.9.6.2; 1.9.6.2 date 2007.06.08.14.17.40; author ad; state Exp; branches; next 1.9.6.3; 1.9.6.3 date 2007.07.01.21.50.47; author ad; state Exp; branches; next 1.9.6.4; 1.9.6.4 date 2007.10.09.13.44.46; author ad; state Exp; branches; next ; 1.9.8.1 date 2007.07.11.20.11.11; author mjf; state Exp; branches; next ; 1.8.2.1 date 2007.07.19.16.04.18; author liamjfoy; state Exp; branches; next 1.8.2.2; 1.8.2.2 date 2010.11.21.21.38.19; author riz; state Exp; branches; next ; 1.8.4.1 date 2007.09.03.07.05.10; author wrstuden; state Exp; branches; next ; 1.5.2.1 date 2006.10.22.06.07.28; author yamt; state Exp; branches; next 1.5.2.2; 1.5.2.2 date 2006.12.10.07.19.06; author yamt; state Exp; branches; next ; 1.3.2.1 date 2006.09.14.21.16.31; author riz; state Exp; branches; next ; 1.3.4.1 date 2006.11.18.21.39.36; author ad; state Exp; branches; next 1.3.4.2; 1.3.4.2 date 2007.01.12.01.04.14; author ad; state Exp; branches; next ; 1.3.6.1 date 2006.07.26.10.20.56; author rpaulo; state dead; branches; next 1.3.6.2; 1.3.6.2 date 2006.09.09.02.58.38; author rpaulo; state Exp; branches; next ; 1.1.2.1 date 2006.06.19.15.44.45; author yamt; state dead; branches; next 1.1.2.2; 1.1.2.2 date 2006.06.21.15.10.51; author yamt; state Exp; branches; next 1.1.2.3; 1.1.2.3 date 2006.12.30.20.50.32; author yamt; state Exp; branches; next 1.1.2.4; 1.1.2.4 date 2007.09.03.14.42.34; author yamt; state Exp; branches; next 1.1.2.5; 1.1.2.5 date 2007.10.27.11.36.06; author yamt; state Exp; branches; next 1.1.2.6; 1.1.2.6 date 2007.11.15.11.45.04; author yamt; state Exp; branches; next 1.1.2.7; 1.1.2.7 date 2008.03.17.09.15.41; author yamt; state Exp; branches; next ; 1.1.4.1 date 2006.06.19.15.44.45; author chap; state dead; branches; next 1.1.4.2; 1.1.4.2 date 2006.06.22.03.39.50; author chap; state Exp; branches; next ; 1.1.6.1 date 2006.06.19.15.44.45; author yamt; state dead; branches; next 1.1.6.2; 1.1.6.2 date 2006.06.26.12.53.57; author yamt; state Exp; branches; next 1.1.6.3; 1.1.6.3 date 2006.08.11.15.46.32; author yamt; state Exp; branches; next 1.1.6.4; 1.1.6.4 date 2006.09.14.12.31.55; author yamt; state Exp; branches; next ; 1.1.8.1 date 2006.06.19.15.44.45; author gdamore; state dead; branches; next 1.1.8.2; 1.1.8.2 date 2006.07.13.17.49.58; author gdamore; state Exp; branches; next ; desc @@ 1.26 log @fix typos in comments and log messages, mainly in establish(ed). @ text @/* $NetBSD: hci_link.c,v 1.25 2018/09/07 14:47:15 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. * Copyright (c) 2006 Itronix Inc. * 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. * 3. The name of Itronix Inc. may not be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. */ #include __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.25 2018/09/07 14:47:15 plunky Exp $"); #include #include #include #include #include #include #include #include #include #include #include /******************************************************************************* * * HCI ACL Connections */ /* * Automatically expire unused ACL connections after this number of * seconds (if zero, do not expire unused connections) [sysctl] */ int hci_acl_expiry = 10; /* seconds */ /* * hci_acl_open(unit, bdaddr) * * open ACL connection to remote bdaddr. Only one ACL connection is permitted * between any two Bluetooth devices, so we look for an existing one before * trying to start a new one. */ struct hci_link * hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; struct hci_memo *memo; hci_create_con_cp cp; int err; KASSERT(unit != NULL); KASSERT(bdaddr != NULL); link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link == NULL) { link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); if (link == NULL) return NULL; } switch(link->hl_state) { case HCI_LINK_CLOSED: /* * open connection to remote device */ memset(&cp, 0, sizeof(cp)); bdaddr_copy(&cp.bdaddr, bdaddr); cp.pkt_type = htole16(unit->hci_packet_type); memo = hci_memo_find(unit, bdaddr); if (memo != NULL) { cp.page_scan_rep_mode = memo->page_scan_rep_mode; cp.page_scan_mode = memo->page_scan_mode; cp.clock_offset = memo->clock_offset; } if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH) cp.accept_role_switch = 1; err = hci_send_cmd(unit, HCI_CMD_CREATE_CON, &cp, sizeof(cp)); if (err) { hci_link_free(link, err); return NULL; } link->hl_flags |= HCI_LINK_CREATE_CON; link->hl_state = HCI_LINK_WAIT_CONNECT; break; case HCI_LINK_WAIT_CONNECT: case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: /* * somebody else already trying to connect, we just * sit on the bench with them.. */ break; case HCI_LINK_OPEN: /* * If already open, halt any expiry timeouts. We don't need * to care about already invoking timeouts since refcnt >0 * will keep the link alive. */ callout_stop(&link->hl_expire); break; default: UNKNOWN(link->hl_state); return NULL; } /* open */ link->hl_refcnt++; return link; } /* * Close ACL connection. When there are no more references to this link, * we can either close it down or schedule a delayed closedown. */ void hci_acl_close(struct hci_link *link, int err) { KASSERT(link != NULL); if (--link->hl_refcnt == 0) { if (link->hl_state == HCI_LINK_CLOSED) hci_link_free(link, err); else if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } } /* * Incoming ACL connection. * * Check the L2CAP listeners list and only accept when there is a * potential listener available. * * There should not be a link to the same bdaddr already, we check * anyway though its left unhandled for now. */ struct hci_link * hci_acl_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; struct l2cap_channel *chan; LIST_FOREACH(chan, &l2cap_listen_list, lc_ncid) { if (bdaddr_same(&unit->hci_bdaddr, &chan->lc_laddr.bt_bdaddr) || bdaddr_any(&chan->lc_laddr.bt_bdaddr)) break; } if (chan == NULL) { DPRINTF("%s: rejecting connection (no listeners)\n", device_xname(unit->hci_dev)); return NULL; } link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link != NULL) { DPRINTF("%s: rejecting connection (link exists)\n", device_xname(unit->hci_dev)); return NULL; } link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); if (link != NULL) { link->hl_state = HCI_LINK_WAIT_CONNECT; if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } return link; } void hci_acl_timeout(void *arg) { struct hci_link *link = arg; hci_discon_cp cp; int err; mutex_enter(bt_lock); callout_ack(&link->hl_expire); if (link->hl_refcnt > 0) goto out; DPRINTF("link #%d expired\n", link->hl_handle); switch (link->hl_state) { case HCI_LINK_CLOSED: case HCI_LINK_WAIT_CONNECT: hci_link_free(link, ECONNRESET); break; case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: case HCI_LINK_OPEN: cp.con_handle = htole16(link->hl_handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ err = hci_send_cmd(link->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); if (err) { DPRINTF("error %d sending HCI_CMD_DISCONNECT\n", err); } break; default: UNKNOWN(link->hl_state); break; } out: mutex_exit(bt_lock); } /* * Initiate any Link Mode change requests. */ int hci_acl_setmode(struct hci_link *link) { int err; KASSERT(link != NULL); KASSERT(link->hl_unit != NULL); if (link->hl_state != HCI_LINK_OPEN) return EINPROGRESS; if ((link->hl_flags & HCI_LINK_AUTH_REQ) && !(link->hl_flags & HCI_LINK_AUTH)) { hci_auth_req_cp cp; DPRINTF("requesting auth for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_AUTH; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) && !(link->hl_flags & HCI_LINK_ENCRYPT)) { hci_set_con_encryption_cp cp; /* XXX we should check features for encryption capability */ DPRINTF("requesting encryption for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_ENCRYPT; cp.con_handle = htole16(link->hl_handle); cp.encryption_enable = 0x01; err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { hci_change_con_link_key_cp cp; /* always change link key for SECURE requests */ link->hl_flags &= ~HCI_LINK_SECURE; DPRINTF("changing link key for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_SECURE; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } return 0; } /* * Link Mode changed. * * This is called from event handlers when the mode change * is complete. We notify upstream and restart the link. */ void hci_acl_linkmode(struct hci_link *link) { struct l2cap_channel *chan, *next; int err, mode = 0; DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", link->hl_handle, (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); if (link->hl_flags & HCI_LINK_AUTH) mode |= L2CAP_LM_AUTH; if (link->hl_flags & HCI_LINK_ENCRYPT) mode |= L2CAP_LM_ENCRYPT; if (link->hl_flags & HCI_LINK_SECURE) mode |= L2CAP_LM_SECURE; /* * The link state will only be OPEN here if the mode change * was successful. So, we can proceed with L2CAP connections, * or notify already established channels, to allow any that * are dissatisfied to disconnect before we restart. */ next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link != link) continue; switch(chan->lc_state) { case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_close(chan, ECONNABORTED); break; } chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; err = l2cap_send_connect_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_send_connect_rsp(link, chan->lc_ident, 0, chan->lc_rcid, L2CAP_SECURITY_BLOCK); l2cap_close(chan, ECONNABORTED); break; } l2cap_send_connect_rsp(link, chan->lc_ident, chan->lc_lcid, chan->lc_rcid, L2CAP_SUCCESS); chan->lc_state = L2CAP_WAIT_CONFIG; chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); err = l2cap_send_config_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_RECV_CONNECT_RSP: case L2CAP_WAIT_CONFIG: case L2CAP_OPEN: /* already established */ (*chan->lc_proto->linkmode)(chan->lc_upper, mode); break; default: break; } } link->hl_state = HCI_LINK_OPEN; hci_acl_start(link); } /* * Receive ACL Data * * we accumulate packet fragments on the hci_link structure * until a full L2CAP frame is ready, then send it on. */ void hci_acl_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_acldata_hdr_t hdr; uint16_t handle, want; int pb, got; KASSERT(m != NULL); KASSERT(unit != NULL); if (m->m_pkthdr.len < sizeof(hdr)) goto bad; m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); KASSERT(hdr.type == HCI_ACL_DATA_PKT); hdr.length = le16toh(hdr.length); hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); pb = HCI_PB_FLAG(hdr.con_handle); if (m->m_pkthdr.len != hdr.length) goto bad; link = hci_link_lookup_handle(unit, handle); if (link == NULL) { hci_discon_cp cp; DPRINTF("%s: dumping packet for unknown handle #%d\n", device_xname(unit->hci_dev), handle); /* * There is no way to find out what this connection handle is * for, just get rid of it. This may happen, if a USB dongle * is plugged into a self powered hub and does not reset when * the system is shut down. * * This can cause a problem with some Broadcom controllers * which emit empty ACL packets during connection setup, so * only disconnect where data is present. */ if (hdr.length > 0) { cp.con_handle = htole16(handle); cp.reason = 0x13;/*"Remote User Terminated Connection"*/ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); } goto bad; } switch (pb) { case HCI_PACKET_START: if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) goto bad; if (link->hl_rxp != NULL) { aprint_error_dev(unit->hci_dev, "dropped incomplete ACL packet\n"); m_freem(link->hl_rxp); } link->hl_rxp = m; got = m->m_pkthdr.len; break; case HCI_PACKET_FRAGMENT: if (link->hl_rxp == NULL) { aprint_error_dev(unit->hci_dev, "unexpected packet fragment\n"); goto bad; } got = m->m_pkthdr.len + link->hl_rxp->m_pkthdr.len; m_cat(link->hl_rxp, m); m = link->hl_rxp; m->m_pkthdr.len = got; break; default: DPRINTF("%s: unknown packet type\n", device_xname(unit->hci_dev)); goto bad; } m_copydata(m, 0, sizeof(want), &want); want = le16toh(want); got -= sizeof(l2cap_hdr_t); if (got < want) /* wait for more */ return; link->hl_rxp = NULL; if (got > want) { DPRINTF("%s: packet overflow\n", device_xname(unit->hci_dev)); goto bad; } l2cap_recv_frame(m, link); return; bad: m_freem(m); } /* * Send ACL data on link * * We must fragment packets into chunks of less than unit->hci_max_acl_size and * prepend a relevant ACL header to each fragment. We keep a PDU structure * attached to the link, so that completed fragments can be marked off and * more data requested from above once the PDU is sent. */ int hci_acl_send(struct mbuf *m, struct hci_link *link, struct l2cap_channel *chan) { struct l2cap_pdu *pdu; struct mbuf *n = NULL; int plen, mlen, num = 0; KASSERT(link != NULL); KASSERT(m != NULL); KASSERT(m->m_flags & M_PKTHDR); KASSERT(m->m_pkthdr.len > 0); if (link->hl_state == HCI_LINK_CLOSED) { m_freem(m); return ENETDOWN; } pdu = pool_get(&l2cap_pdu_pool, PR_NOWAIT); if (pdu == NULL) goto nomem; pdu->lp_chan = chan; pdu->lp_pending = 0; MBUFQ_INIT(&pdu->lp_data); plen = m->m_pkthdr.len; mlen = link->hl_unit->hci_max_acl_size; DPRINTFN(5, "%s: handle #%d, plen = %d, max = %d\n", device_xname(link->hl_unit->hci_dev), link->hl_handle, plen, mlen); while (plen > 0) { if (plen > mlen) { n = m_split(m, mlen, M_DONTWAIT); if (n == NULL) goto nomem; } else { mlen = plen; } if (num++ == 0) m->m_flags |= M_PROTO1; /* tag first fragment */ DPRINTFN(10, "chunk of %d (plen = %d) bytes\n", mlen, plen); MBUFQ_ENQUEUE(&pdu->lp_data, m); m = n; plen -= mlen; } TAILQ_INSERT_TAIL(&link->hl_txq, pdu, lp_next); link->hl_txqlen += num; hci_acl_start(link); return 0; nomem: if (m) m_freem(m); if (pdu) { MBUFQ_DRAIN(&pdu->lp_data); pool_put(&l2cap_pdu_pool, pdu); } return ENOMEM; } /* * Start sending ACL data on link. * * This is called when the queue may need restarting: as new data * is queued, after link mode changes have completed, or when device * buffers have cleared. * * We may use all the available packet slots. The reason that we add * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP * signal packets may be queued before the handle is given to us.. */ void hci_acl_start(struct hci_link *link) { struct hci_unit *unit; hci_acldata_hdr_t *hdr; struct l2cap_pdu *pdu; struct mbuf *m; uint16_t handle; KASSERT(link != NULL); unit = link->hl_unit; KASSERT(unit != NULL); /* this is mainly to block ourselves (below) */ if (link->hl_state != HCI_LINK_OPEN) return; if (link->hl_txqlen == 0 || unit->hci_num_acl_pkts == 0) return; /* find first PDU with data to send */ pdu = TAILQ_FIRST(&link->hl_txq); for (;;) { if (pdu == NULL) return; if (MBUFQ_FIRST(&pdu->lp_data) != NULL) break; pdu = TAILQ_NEXT(pdu, lp_next); } while (unit->hci_num_acl_pkts > 0) { MBUFQ_DEQUEUE(&pdu->lp_data, m); KASSERT(m != NULL); if (m->m_flags & M_PROTO1) handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_START, 0); else handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_FRAGMENT, 0); M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) break; hdr = mtod(m, hci_acldata_hdr_t *); hdr->type = HCI_ACL_DATA_PKT; hdr->con_handle = htole16(handle); hdr->length = htole16(m->m_pkthdr.len - sizeof(*hdr)); link->hl_txqlen--; pdu->lp_pending++; hci_output_acl(unit, m); if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { if (pdu->lp_chan) { /* * This should enable streaming of PDUs - when * we have placed all the fragments on the acl * output queue, we trigger the L2CAP layer to * send us down one more. Use a false state so * we dont run into ourselves coming back from * the future.. */ link->hl_state = HCI_LINK_BLOCK; l2cap_start(pdu->lp_chan); link->hl_state = HCI_LINK_OPEN; } pdu = TAILQ_NEXT(pdu, lp_next); if (pdu == NULL) break; } } /* * We had our turn now, move to the back of the queue to let * other links have a go at the output buffers.. */ if (TAILQ_NEXT(link, hl_next)) { TAILQ_REMOVE(&unit->hci_links, link, hl_next); TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); } } /* * Confirm ACL packets cleared from Controller buffers. We scan our PDU * list to clear pending fragments and signal upstream for more data * when a PDU is complete. */ void hci_acl_complete(struct hci_link *link, int num) { struct l2cap_pdu *pdu; struct l2cap_channel *chan; DPRINTFN(5, "handle #%d (%d)\n", link->hl_handle, num); while (num > 0) { pdu = TAILQ_FIRST(&link->hl_txq); if (pdu == NULL) { aprint_error_dev(link->hl_unit->hci_dev, "%d packets completed on handle #%x but none pending!\n", num, link->hl_handle); return; } if (num >= pdu->lp_pending) { num -= pdu->lp_pending; pdu->lp_pending = 0; if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); chan = pdu->lp_chan; if (chan != NULL) { chan->lc_pending--; (*chan->lc_proto->complete) (chan->lc_upper, 1); if (chan->lc_pending == 0) l2cap_start(chan); } pool_put(&l2cap_pdu_pool, pdu); } } else { pdu->lp_pending -= num; num = 0; } } } /******************************************************************************* * * HCI SCO Connections */ /* * Incoming SCO Connection. We check the list for anybody willing * to take it. */ struct hci_link * hci_sco_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct sockaddr_bt laddr, raddr; struct sco_pcb *pcb, *new; struct hci_link *sco, *acl; memset(&laddr, 0, sizeof(laddr)); laddr.bt_len = sizeof(laddr); laddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&laddr.bt_bdaddr, &unit->hci_bdaddr); memset(&raddr, 0, sizeof(raddr)); raddr.bt_len = sizeof(raddr); raddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&raddr.bt_bdaddr, bdaddr); /* * There should already be an ACL link up and running before * the controller sends us SCO connection requests, but you * never know.. */ acl = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (acl == NULL || acl->hl_state != HCI_LINK_OPEN) return NULL; LIST_FOREACH(pcb, &sco_pcb, sp_next) { if ((pcb->sp_flags & SP_LISTENING) == 0) continue; new = (*pcb->sp_proto->newconn)(pcb->sp_upper, &laddr, &raddr); if (new == NULL) continue; /* * Ok, got new pcb so we can start a new link and fill * in all the details. */ bdaddr_copy(&new->sp_laddr, &unit->hci_bdaddr); bdaddr_copy(&new->sp_raddr, bdaddr); sco = hci_link_alloc(unit, bdaddr, HCI_LINK_SCO); if (sco == NULL) { sco_detach_pcb(&new); return NULL; } sco->hl_link = hci_acl_open(unit, bdaddr); KASSERT(sco->hl_link == acl); sco->hl_sco = new; new->sp_link = sco; new->sp_mtu = unit->hci_max_sco_size; return sco; } return NULL; } /* * receive SCO packet, we only need to strip the header and send * it to the right handler */ void hci_sco_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_scodata_hdr_t hdr; uint16_t handle; KASSERT(m != NULL); KASSERT(unit != NULL); if (m->m_pkthdr.len < sizeof(hdr)) goto bad; m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); KASSERT(hdr.type == HCI_SCO_DATA_PKT); hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); if (m->m_pkthdr.len != hdr.length) goto bad; link = hci_link_lookup_handle(unit, handle); if (link == NULL || link->hl_type == HCI_LINK_ACL) { DPRINTF("%s: dumping packet for unknown handle #%d\n", device_xname(unit->hci_dev), handle); goto bad; } (*link->hl_sco->sp_proto->input)(link->hl_sco->sp_upper, m); return; bad: m_freem(m); } void hci_sco_start(struct hci_link *link) { } /* * SCO packets have completed at the controller, so we can * signal up to free the buffer space. */ void hci_sco_complete(struct hci_link *link, int num) { DPRINTFN(5, "handle #%d (num=%d)\n", link->hl_handle, num); link->hl_sco->sp_pending--; (*link->hl_sco->sp_proto->complete)(link->hl_sco->sp_upper, num); } /******************************************************************************* * * Generic HCI Connection alloc/free/lookup etc */ struct hci_link * hci_link_alloc(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) { struct hci_link *link; KASSERT(unit != NULL); link = malloc(sizeof(struct hci_link), M_BLUETOOTH, M_NOWAIT | M_ZERO); if (link == NULL) return NULL; link->hl_unit = unit; link->hl_type = type; link->hl_state = HCI_LINK_CLOSED; bdaddr_copy(&link->hl_bdaddr, bdaddr); /* init ACL portion */ callout_init(&link->hl_expire, 0); callout_setfunc(&link->hl_expire, hci_acl_timeout, link); TAILQ_INIT(&link->hl_txq); /* outgoing packets */ TAILQ_INIT(&link->hl_reqs); /* request queue */ link->hl_mtu = L2CAP_MTU_DEFAULT; /* L2CAP signal mtu */ link->hl_flush = L2CAP_FLUSH_TIMO_DEFAULT; /* flush timeout */ /* init SCO portion */ MBUFQ_INIT(&link->hl_data); /* attach to unit */ TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); return link; } void hci_link_free(struct hci_link *link, int err) { struct l2cap_req *req; struct l2cap_pdu *pdu; struct l2cap_channel *chan, *next; KASSERT(link != NULL); DPRINTF("#%d, type = %d, state = %d, refcnt = %d\n", link->hl_handle, link->hl_type, link->hl_state, link->hl_refcnt); /* ACL reference count */ if (link->hl_refcnt > 0) { next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link == link) l2cap_close(chan, err); } } KASSERT(link->hl_refcnt == 0); /* ACL L2CAP requests.. */ while ((req = TAILQ_FIRST(&link->hl_reqs)) != NULL) l2cap_request_free(req); KASSERT(TAILQ_EMPTY(&link->hl_reqs)); /* ACL outgoing data queue */ while ((pdu = TAILQ_FIRST(&link->hl_txq)) != NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); MBUFQ_DRAIN(&pdu->lp_data); if (pdu->lp_pending) link->hl_unit->hci_num_acl_pkts += pdu->lp_pending; pool_put(&l2cap_pdu_pool, pdu); } KASSERT(TAILQ_EMPTY(&link->hl_txq)); /* ACL incoming data packet */ if (link->hl_rxp != NULL) { m_freem(link->hl_rxp); link->hl_rxp = NULL; } /* SCO master ACL link */ if (link->hl_link != NULL) { hci_acl_close(link->hl_link, err); link->hl_link = NULL; } /* SCO pcb */ if (link->hl_sco != NULL) { struct sco_pcb *pcb; pcb = link->hl_sco; pcb->sp_link = NULL; link->hl_sco = NULL; (*pcb->sp_proto->disconnected)(pcb->sp_upper, err); } /* flush any SCO data */ MBUFQ_DRAIN(&link->hl_data); /* * Halt the callout - if its already running we cannot free the * link structure but the timeout function will call us back in * any case. */ link->hl_state = HCI_LINK_CLOSED; callout_stop(&link->hl_expire); if (callout_invoking(&link->hl_expire)) return; callout_destroy(&link->hl_expire); /* * If we made a note of clock offset, keep it in a memo * to facilitate reconnections to this device */ if (link->hl_clock != 0) { struct hci_memo *memo; memo = hci_memo_new(link->hl_unit, &link->hl_bdaddr); if (memo != NULL) memo->clock_offset = link->hl_clock; } TAILQ_REMOVE(&link->hl_unit->hci_links, link, hl_next); free(link, M_BLUETOOTH); } /* * Lookup HCI link by address and type. Note that for SCO links there may * be more than one link per address, so we only return links with no * handle (ie new links) */ struct hci_link * hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) { struct hci_link *link; KASSERT(unit != NULL); KASSERT(bdaddr != NULL); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (link->hl_type != type) continue; if (type == HCI_LINK_SCO && link->hl_handle != 0) continue; if (bdaddr_same(&link->hl_bdaddr, bdaddr)) break; } return link; } struct hci_link * hci_link_lookup_handle(struct hci_unit *unit, uint16_t handle) { struct hci_link *link; KASSERT(unit != NULL); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (handle == link->hl_handle) break; } return link; } @ 1.25 log @two issues noted by maxv@@ 1. If an adaptor sends repeated fragments indicating HCI_PACKET_START, we would leak mbufs. Fix that by releasing the previous in that case. 2. If an adaptor sends fragments which overflow the expected total payload length, it could build up the pending packet to use up system mbufs. Fix that by changing the unsigned calculation to a comparison and rejecting oversize packets @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.24 2014/05/20 18:25:54 rmind Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.24 2014/05/20 18:25:54 rmind Exp $"); d126 1 a126 1 * If already open, halt any expiry timeouts. We dont need d355 1 a355 1 * or notify already establshed channels, to allow any that @ 1.24 log @netbt: rename some attach/detach functions to have _pcb suffix, so we could use standard attach/detach naming for pr_usrreq functions. No functional change. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.23 2011/07/27 10:25:09 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.23 2011/07/27 10:25:09 plunky Exp $"); d478 4 a481 1 if (link->hl_rxp != NULL) d485 2 a486 2 if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) goto bad; d514 2 a515 1 want = le16toh(want) + sizeof(l2cap_hdr_t) - got; d517 1 a517 1 if (want > 0) d522 5 a526 3 if (want == 0) { l2cap_recv_frame(m, link); return; d529 3 @ 1.24.28.1 log @Sync with HEAD @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.25 2018/09/07 14:47:15 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.25 2018/09/07 14:47:15 plunky Exp $"); d478 1 a478 4 if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) goto bad; if (link->hl_rxp != NULL) { d482 2 a483 2 m_freem(link->hl_rxp); } d511 1 a511 2 want = le16toh(want); got -= sizeof(l2cap_hdr_t); d513 1 a513 1 if (got < want) /* wait for more */ d518 3 a520 5 if (got > want) { DPRINTF("%s: packet overflow\n", device_xname(unit->hci_dev)); goto bad; a522 3 l2cap_recv_frame(m, link); return; @ 1.24.26.1 log @Ssync with HEAD @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.25 2018/09/07 14:47:15 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.25 2018/09/07 14:47:15 plunky Exp $"); d478 1 a478 4 if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) goto bad; if (link->hl_rxp != NULL) { d482 2 a483 2 m_freem(link->hl_rxp); } d511 1 a511 2 want = le16toh(want); got -= sizeof(l2cap_hdr_t); d513 1 a513 1 if (got < want) /* wait for more */ d518 3 a520 5 if (got > want) { DPRINTF("%s: packet overflow\n", device_xname(unit->hci_dev)); goto bad; a522 3 l2cap_recv_frame(m, link); return; @ 1.23 log @ cleanup some DIAGNOSTIC and KASSERT code - remove #ifdef DIAGNOSTIC, so that we won't act differently - handle the cases where a Bluetooth adapter sends invalid packet data (I've not seen this, but it is not impossible) - use KASSERT for actual impossible situations (to catch bad future development) @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.22 2010/10/14 07:05:03 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.22 2010/10/14 07:05:03 plunky Exp $"); d802 1 a802 1 sco_detach(&new); @ 1.23.12.1 log @Rebase to HEAD as of a few days ago. @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); d802 1 a802 1 sco_detach_pcb(&new); @ 1.23.26.1 log @Rebase. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.24 2014/05/20 18:25:54 rmind Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.24 2014/05/20 18:25:54 rmind Exp $"); d802 1 a802 1 sco_detach_pcb(&new); @ 1.22 log @Some Broadcom controllers emit empty ACL packets during connection setup, using the handle that they have not yet told us for the connection-to-be. Disconnecting can cause problems so just ignore zero length ACL packets on unknown connection handles. fixes a problem reported by Nick Hudson @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.21 2009/09/24 19:35:09 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.21 2009/09/24 19:35:09 plunky Exp $"); d435 3 a437 1 KASSERT(m->m_pkthdr.len >= sizeof(hdr)); d441 1 a441 13 #ifdef DIAGNOSTIC if (hdr.type != HCI_ACL_DATA_PKT) { aprint_error_dev(unit->hci_dev, "bad ACL packet type\n"); goto bad; } if (m->m_pkthdr.len != le16toh(hdr.length)) { aprint_error_dev(unit->hci_dev, "bad ACL packet length (%d != %d)\n", m->m_pkthdr.len, le16toh(hdr.length)); goto bad; } #endif d448 3 d482 1 a482 2 if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) { aprint_error_dev(unit->hci_dev, "short ACL packet\n"); a483 1 } d504 3 a506 1 aprint_error_dev(unit->hci_dev, "unknown packet type\n"); d833 3 a835 1 KASSERT(m->m_pkthdr.len >= sizeof(hdr)); d839 1 a839 5 #ifdef DIAGNOSTIC if (hdr.type != HCI_SCO_DATA_PKT) { aprint_error_dev(unit->hci_dev, "bad SCO packet type\n"); goto bad; } d841 2 a842 4 if (m->m_pkthdr.len != hdr.length) { aprint_error_dev(unit->hci_dev, "bad SCO packet length (%d != %d)\n", m->m_pkthdr.len, hdr.length); d844 1 a845 5 } #endif hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); @ 1.21 log @Only accept incoming ACL connections when there is potential L2CAP listener available. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.20 2008/04/24 11:38:37 ad Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.20 2008/04/24 11:38:37 ad Exp $"); d470 4 d475 5 a479 3 cp.con_handle = htole16(handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); @ 1.21.4.1 log @sync with head @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); a469 4 * * This can cause a problem with some Broadcom controllers * which emit empty ACL packets during connection setup, so * only disconnect where data is present. d471 3 a473 5 if (hdr.length > 0) { cp.con_handle = htole16(handle); cp.reason = 0x13;/*"Remote User Terminated Connection"*/ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); } @ 1.21.2.1 log @Sync with HEAD (-D20101022). @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); a469 4 * * This can cause a problem with some Broadcom controllers * which emit empty ACL packets during connection setup, so * only disconnect where data is present. d471 3 a473 5 if (hdr.length > 0) { cp.con_handle = htole16(handle); cp.reason = 0x13;/*"Remote User Terminated Connection"*/ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); } @ 1.20 log @Merge the socket locking patch: - Socket layer becomes MP safe. - Unix protocols become MP safe. - Allows protocol processing interrupts to safely block on locks. - Fixes a number of race conditions. With much feedback from matt@@ and plunky@@. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.19 2008/03/16 23:28:10 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.19 2008/03/16 23:28:10 plunky Exp $"); d165 2 a166 3 * For now, we accept all connections but it would be better to check * the L2CAP listen list and only accept when there is a listener * available. d175 14 d191 4 a194 1 if (link != NULL) d196 1 @ 1.20.12.1 log @Pull up following revision(s) (requested by plunky in ticket #1461): sys/netbt/hci_link.c: revision 1.22 Some Broadcom controllers emit empty ACL packets during connection setup, using the handle that they have not yet told us for the connection-to-be. Disconnecting can cause problems so just ignore zero length ACL packets on unknown connection handles. fixes a problem reported by Nick Hudson @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); a452 4 * * This can cause a problem with some Broadcom controllers * which emit empty ACL packets during connection setup, so * only disconnect where data is present. d454 3 a456 5 if (hdr.length > 0) { cp.con_handle = htole16(handle); cp.reason = 0x13;/*"Remote User Terminated Connection"*/ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); } @ 1.20.2.1 log @sync with head @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.20 2008/04/24 11:38:37 ad Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.20 2008/04/24 11:38:37 ad Exp $"); d165 3 a167 2 * Check the L2CAP listeners list and only accept when there is a * potential listener available. a175 14 struct l2cap_channel *chan; LIST_FOREACH(chan, &l2cap_listen_list, lc_ncid) { if (bdaddr_same(&unit->hci_bdaddr, &chan->lc_laddr.bt_bdaddr) || bdaddr_any(&chan->lc_laddr.bt_bdaddr)) break; } if (chan == NULL) { DPRINTF("%s: rejecting connection (no listeners)\n", device_xname(unit->hci_dev)); return NULL; } d178 1 a178 4 if (link != NULL) { DPRINTF("%s: rejecting connection (link exists)\n", device_xname(unit->hci_dev)); a179 1 } @ 1.19 log @we always know the bdaddr and type of a link when creating it, so pass them to the _alloc() function and let it set them. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.18 2008/03/16 23:14:24 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.18 2008/03/16 23:14:24 plunky Exp $"); d197 1 a197 1 int s, err; d199 1 a199 1 s = splsoftnet(); d236 1 a236 1 splx(s); @ 1.19.2.1 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.19 2008/03/16 23:28:10 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.19 2008/03/16 23:28:10 plunky Exp $"); d197 1 a197 1 int err; d199 1 a199 1 mutex_enter(bt_lock); d236 1 a236 1 mutex_exit(bt_lock); @ 1.18 log @insert new links at the tail of the queue so that if a create_connection command fails to start we can find the relevant link, since it will be the first one with the pending flag set. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.17 2008/03/06 20:56:26 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.17 2008/03/06 20:56:26 plunky Exp $"); d80 1 a80 1 link = hci_link_alloc(unit); a82 3 link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); d181 1 a181 1 link = hci_link_alloc(unit); a183 2 link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); d784 1 a784 1 sco = hci_link_alloc(unit); a789 3 sco->hl_type = HCI_LINK_SCO; bdaddr_copy(&sco->hl_bdaddr, bdaddr); d878 1 a878 1 hci_link_alloc(struct hci_unit *unit) d889 1 d891 1 d1012 1 a1012 1 hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint16_t type) @ 1.17 log @a "Create Connection" command can sometimes fail to start for whatever reason and the command_status event returns failure but we get no indication of which connection failed (for instance in the case where we tried to open too many connections all at once) So, keep a flag on the link to indicate pending status until the command_status event is returned to help us decide which should be failed. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.16 2007/11/10 23:12:22 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.16 2007/11/10 23:12:22 plunky Exp $"); d913 1 a913 1 TAILQ_INSERT_HEAD(&unit->hci_links, link, hl_next); @ 1.16 log @use more device_t and device_xxx() accessors make bluetooth stack keep device_t instead of softc pointer as device is not necessarily part of softc, and pass device_t to driver callbacks. hci_devname is no longer required. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.15 2007/11/03 17:20:17 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.15 2007/11/03 17:20:17 plunky Exp $"); d113 1 @ 1.16.14.1 log @Sync with HEAD. @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); d80 1 a80 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d83 3 a112 1 link->hl_flags |= HCI_LINK_CREATE_CON; d183 1 a183 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d186 2 d788 1 a788 1 sco = hci_link_alloc(unit, bdaddr, HCI_LINK_SCO); d794 3 d885 1 a885 1 hci_link_alloc(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) a895 1 link->hl_type = type; a896 1 bdaddr_copy(&link->hl_bdaddr, bdaddr); d912 1 a912 1 TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); d1017 1 a1017 1 hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) @ 1.16.14.2 log @Sync with HEAD. @ text @d197 1 a197 1 int err; d199 1 a199 1 mutex_enter(bt_lock); d236 1 a236 1 mutex_exit(bt_lock); @ 1.16.10.1 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.19 2008/03/16 23:28:10 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.19 2008/03/16 23:28:10 plunky Exp $"); d80 1 a80 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d83 3 a112 1 link->hl_flags |= HCI_LINK_CREATE_CON; d183 1 a183 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d186 2 d788 1 a788 1 sco = hci_link_alloc(unit, bdaddr, HCI_LINK_SCO); d794 3 d885 1 a885 1 hci_link_alloc(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) a895 1 link->hl_type = type; a896 1 bdaddr_copy(&link->hl_bdaddr, bdaddr); d912 1 a912 1 TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); d1017 1 a1017 1 hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) @ 1.15 log @"struct callout" -> callout_t don't use callout_reset() do use callout_destroy() @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.14 2007/09/16 19:59:30 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.14 2007/09/16 19:59:30 plunky Exp $"); d428 1 a428 1 printf("%s: bad ACL packet type\n", unit->hci_devname); d433 3 a435 2 printf("%s: bad ACL packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, le16toh(hdr.length)); d450 1 a450 1 unit->hci_devname, handle); d467 2 a468 2 printf("%s: dropped incomplete ACL packet\n", unit->hci_devname); d471 1 a471 3 printf("%s: short ACL packet\n", unit->hci_devname); d481 2 a482 2 printf("%s: unexpected packet fragment\n", unit->hci_devname); d494 1 a494 3 printf("%s: unknown packet type\n", unit->hci_devname); d553 1 a553 1 link->hl_unit->hci_devname, link->hl_handle, plen, mlen); d706 4 a709 4 printf("%s: %d packets completed on handle #%x " "but none pending!\n", link->hl_unit->hci_devname, num, link->hl_handle); d830 1 a830 1 printf("%s: bad SCO packet type\n", unit->hci_devname); d835 4 a838 1 printf("%s: bad SCO packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, hdr.length); d849 1 a849 1 unit->hci_devname, handle); @ 1.15.2.1 log @Sync with HEAD. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.16 2007/11/10 23:12:22 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.16 2007/11/10 23:12:22 plunky Exp $"); d428 1 a428 1 aprint_error_dev(unit->hci_dev, "bad ACL packet type\n"); d433 2 a434 3 aprint_error_dev(unit->hci_dev, "bad ACL packet length (%d != %d)\n", m->m_pkthdr.len, le16toh(hdr.length)); d449 1 a449 1 device_xname(unit->hci_dev), handle); d466 2 a467 2 aprint_error_dev(unit->hci_dev, "dropped incomplete ACL packet\n"); d470 3 a472 1 aprint_error_dev(unit->hci_dev, "short ACL packet\n"); d482 2 a483 2 aprint_error_dev(unit->hci_dev, "unexpected packet fragment\n"); d495 3 a497 1 aprint_error_dev(unit->hci_dev, "unknown packet type\n"); d556 1 a556 1 device_xname(link->hl_unit->hci_dev), link->hl_handle, plen, mlen); d709 4 a712 4 aprint_error_dev(link->hl_unit->hci_dev, "%d packets completed on handle #%x but none pending!\n", num, link->hl_handle); d833 1 a833 1 aprint_error_dev(unit->hci_dev, "bad SCO packet type\n"); d838 1 a838 4 aprint_error_dev(unit->hci_dev, "bad SCO packet length (%d != %d)\n", m->m_pkthdr.len, hdr.length); d849 1 a849 1 device_xname(unit->hci_dev), handle); @ 1.14 log @improve memo taking of known bluetooth devices - centralise creation of new memo into function hci_memo_new(), when a memo exists for that address, just update the timestamp. - all results of inquiry/rssi result are processed; even if no memo can be allocated, we may update a timestamp. - for new connections, query the clock offset of the remote device, in order that we can use it to facilitate future reconnections - as a connection is removed, make a memo of the clock offset @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.13 2007/09/07 18:37:31 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.13 2007/09/07 18:37:31 plunky Exp $"); d993 2 @ 1.14.4.1 log @Sync with HEAD @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); d428 1 a428 1 aprint_error_dev(unit->hci_dev, "bad ACL packet type\n"); d433 2 a434 3 aprint_error_dev(unit->hci_dev, "bad ACL packet length (%d != %d)\n", m->m_pkthdr.len, le16toh(hdr.length)); d449 1 a449 1 device_xname(unit->hci_dev), handle); d466 2 a467 2 aprint_error_dev(unit->hci_dev, "dropped incomplete ACL packet\n"); d470 3 a472 1 aprint_error_dev(unit->hci_dev, "short ACL packet\n"); d482 2 a483 2 aprint_error_dev(unit->hci_dev, "unexpected packet fragment\n"); d495 3 a497 1 aprint_error_dev(unit->hci_dev, "unknown packet type\n"); d556 1 a556 1 device_xname(link->hl_unit->hci_dev), link->hl_handle, plen, mlen); d709 4 a712 4 aprint_error_dev(link->hl_unit->hci_dev, "%d packets completed on handle #%x but none pending!\n", num, link->hl_handle); d833 1 a833 1 aprint_error_dev(unit->hci_dev, "bad SCO packet type\n"); d838 1 a838 4 aprint_error_dev(unit->hci_dev, "bad SCO packet length (%d != %d)\n", m->m_pkthdr.len, hdr.length); d849 1 a849 1 device_xname(unit->hci_dev), handle); a992 2 callout_destroy(&link->hl_expire); @ 1.13 log @add event processing for "Inquiry result with RSSI", and modify the memo contents so that this will fit. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.12 2007/07/09 21:11:10 ad Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.12 2007/07/09 21:11:10 ad Exp $"); d993 12 @ 1.12 log @Merge some of the less invasive changes from the vmlocking branch: - kthread, callout, devsw API changes - select()/poll() improvements - miscellaneous MT safety improvements @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.11 2007/04/21 06:15:23 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.11 2007/04/21 06:15:23 plunky Exp $"); d99 3 a101 3 cp.page_scan_rep_mode = memo->response.page_scan_rep_mode; cp.page_scan_mode = memo->response.page_scan_mode; cp.clock_offset = htole16(memo->response.clock_offset); @ 1.12.8.1 log @sync with HEAD @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.15 2007/11/03 17:20:17 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.15 2007/11/03 17:20:17 plunky Exp $"); d99 3 a101 3 cp.page_scan_rep_mode = memo->page_scan_rep_mode; cp.page_scan_mode = memo->page_scan_mode; cp.clock_offset = memo->clock_offset; a992 14 callout_destroy(&link->hl_expire); /* * If we made a note of clock offset, keep it in a memo * to facilitate reconnections to this device */ if (link->hl_clock != 0) { struct hci_memo *memo; memo = hci_memo_new(link->hl_unit, &link->hl_bdaddr); if (memo != NULL) memo->clock_offset = link->hl_clock; } @ 1.12.8.2 log @sync with HEAD @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.12.8.1 2007/11/06 23:33:42 matt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.12.8.1 2007/11/06 23:33:42 matt Exp $"); d428 1 a428 1 aprint_error_dev(unit->hci_dev, "bad ACL packet type\n"); d433 2 a434 3 aprint_error_dev(unit->hci_dev, "bad ACL packet length (%d != %d)\n", m->m_pkthdr.len, le16toh(hdr.length)); d449 1 a449 1 device_xname(unit->hci_dev), handle); d466 2 a467 2 aprint_error_dev(unit->hci_dev, "dropped incomplete ACL packet\n"); d470 3 a472 1 aprint_error_dev(unit->hci_dev, "short ACL packet\n"); d482 2 a483 2 aprint_error_dev(unit->hci_dev, "unexpected packet fragment\n"); d495 3 a497 1 aprint_error_dev(unit->hci_dev, "unknown packet type\n"); d556 1 a556 1 device_xname(link->hl_unit->hci_dev), link->hl_handle, plen, mlen); d709 4 a712 4 aprint_error_dev(link->hl_unit->hci_dev, "%d packets completed on handle #%x but none pending!\n", num, link->hl_handle); d833 1 a833 1 aprint_error_dev(unit->hci_dev, "bad SCO packet type\n"); d838 1 a838 4 aprint_error_dev(unit->hci_dev, "bad SCO packet length (%d != %d)\n", m->m_pkthdr.len, hdr.length); d849 1 a849 1 device_xname(unit->hci_dev), handle); @ 1.12.8.3 log @sync with HEAD @ text @d1 1 a1 1 /* hci_link.c,v 1.12.8.2 2008/01/09 01:57:22 matt Exp */ d34 1 a34 1 __KERNEL_RCSID(0, "hci_link.c,v 1.12.8.2 2008/01/09 01:57:22 matt Exp"); d80 1 a80 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d83 3 a112 1 link->hl_flags |= HCI_LINK_CREATE_CON; d183 1 a183 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d186 2 d788 1 a788 1 sco = hci_link_alloc(unit, bdaddr, HCI_LINK_SCO); d794 3 d885 1 a885 1 hci_link_alloc(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) a895 1 link->hl_type = type; a896 1 bdaddr_copy(&link->hl_bdaddr, bdaddr); d912 1 a912 1 TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); d1017 1 a1017 1 hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) @ 1.12.6.1 log @Sync with HEAD. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.14 2007/09/16 19:59:30 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.14 2007/09/16 19:59:30 plunky Exp $"); d99 3 a101 3 cp.page_scan_rep_mode = memo->page_scan_rep_mode; cp.page_scan_mode = memo->page_scan_mode; cp.clock_offset = memo->clock_offset; a992 12 /* * If we made a note of clock offset, keep it in a memo * to facilitate reconnections to this device */ if (link->hl_clock != 0) { struct hci_memo *memo; memo = hci_memo_new(link->hl_unit, &link->hl_bdaddr); if (memo != NULL) memo->clock_offset = link->hl_clock; } @ 1.12.6.2 log @Sync with HEAD. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.15 2007/11/03 17:20:17 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.15 2007/11/03 17:20:17 plunky Exp $"); a992 2 callout_destroy(&link->hl_expire); @ 1.12.6.3 log @Sync with HEAD. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.12.6.2 2007/11/04 21:03:36 jmcneill Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.12.6.2 2007/11/04 21:03:36 jmcneill Exp $"); d428 1 a428 1 aprint_error_dev(unit->hci_dev, "bad ACL packet type\n"); d433 2 a434 3 aprint_error_dev(unit->hci_dev, "bad ACL packet length (%d != %d)\n", m->m_pkthdr.len, le16toh(hdr.length)); d449 1 a449 1 device_xname(unit->hci_dev), handle); d466 2 a467 2 aprint_error_dev(unit->hci_dev, "dropped incomplete ACL packet\n"); d470 3 a472 1 aprint_error_dev(unit->hci_dev, "short ACL packet\n"); d482 2 a483 2 aprint_error_dev(unit->hci_dev, "unexpected packet fragment\n"); d495 3 a497 1 aprint_error_dev(unit->hci_dev, "unknown packet type\n"); d556 1 a556 1 device_xname(link->hl_unit->hci_dev), link->hl_handle, plen, mlen); d709 4 a712 4 aprint_error_dev(link->hl_unit->hci_dev, "%d packets completed on handle #%x but none pending!\n", num, link->hl_handle); d833 1 a833 1 aprint_error_dev(unit->hci_dev, "bad SCO packet type\n"); d838 1 a838 4 aprint_error_dev(unit->hci_dev, "bad SCO packet length (%d != %d)\n", m->m_pkthdr.len, hdr.length); d849 1 a849 1 device_xname(unit->hci_dev), handle); @ 1.12.2.1 log @Sync with HEAD. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.13 2007/09/07 18:37:31 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.13 2007/09/07 18:37:31 plunky Exp $"); d99 3 a101 3 cp.page_scan_rep_mode = memo->page_scan_rep_mode; cp.page_scan_mode = memo->page_scan_mode; cp.clock_offset = memo->clock_offset; @ 1.11 log @Add 'service level' security for L2CAP and RFCOMM connections, following the Linux (BlueZ) API. - L2CAP or RFCOMM connections can require the baseband radio link mode be any of: authenticated (devices are paired) encrypted (implies authentication) secured (encryption, plus generate new link key) - for sockets, the mode is set using setsockopt(2) and the socket connection will be aborted if the mode change fails. - mode settings will be applied during connection establishment, and for safety, we enter a wait state and will only proceed when the mode settings are successfuly set. - It is possible to change the mode on already open connections, but not possible to guarantee that data already queued (from either end) will not be delivered. (this is a feature, not a bug) - bthidev(4) and rfcomm_sppd(1) support "auth", "encrypt" and "secure" options - btdevctl(8) by default enables "auth" for HIDs, and "encrypt" for keyboards (which are required to support it) @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.10 2007/03/30 20:47:02 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.10 2007/03/30 20:47:02 plunky Exp $"); d899 1 a899 1 callout_init(&link->hl_expire); @ 1.10 log @be more explicit and consistent in use of KASSERT with pointers, test against NULL @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $"); d117 3 d217 3 d244 162 d596 4 a602 3 * * this is called from hci_acl_send() above, and the event processing * code (for CON_COMPL and NUM_COMPL_PKTS) @ 1.9 log @CID-3819: `n' is always NULL here, remove dead code. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.8 2006/11/16 01:33:45 christos Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.8 2006/11/16 01:33:45 christos Exp $"); d75 2 a76 2 KASSERT(unit); KASSERT(bdaddr); d151 1 a151 1 KASSERT(link); d251 2 a252 2 KASSERT(m); KASSERT(unit); d366 2 a367 2 KASSERT(link); KASSERT(m); d444 1 a444 1 KASSERT(link); d447 1 a447 1 KASSERT(unit); d655 2 a656 2 KASSERT(m); KASSERT(unit); d720 1 a720 1 KASSERT(unit); d754 1 a754 1 KASSERT(link); d838 2 a839 2 KASSERT(unit); KASSERT(bdaddr); d860 1 a860 1 KASSERT(unit); @ 1.9.8.1 log @Sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.12 2007/07/09 21:11:10 ad Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.12 2007/07/09 21:11:10 ad Exp $"); d75 2 a76 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); a116 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: d151 1 a151 1 KASSERT(link != NULL); a213 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a237 162 * Initiate any Link Mode change requests. */ int hci_acl_setmode(struct hci_link *link) { int err; KASSERT(link != NULL); KASSERT(link->hl_unit != NULL); if (link->hl_state != HCI_LINK_OPEN) return EINPROGRESS; if ((link->hl_flags & HCI_LINK_AUTH_REQ) && !(link->hl_flags & HCI_LINK_AUTH)) { hci_auth_req_cp cp; DPRINTF("requesting auth for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_AUTH; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) && !(link->hl_flags & HCI_LINK_ENCRYPT)) { hci_set_con_encryption_cp cp; /* XXX we should check features for encryption capability */ DPRINTF("requesting encryption for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_ENCRYPT; cp.con_handle = htole16(link->hl_handle); cp.encryption_enable = 0x01; err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { hci_change_con_link_key_cp cp; /* always change link key for SECURE requests */ link->hl_flags &= ~HCI_LINK_SECURE; DPRINTF("changing link key for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_SECURE; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } return 0; } /* * Link Mode changed. * * This is called from event handlers when the mode change * is complete. We notify upstream and restart the link. */ void hci_acl_linkmode(struct hci_link *link) { struct l2cap_channel *chan, *next; int err, mode = 0; DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", link->hl_handle, (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); if (link->hl_flags & HCI_LINK_AUTH) mode |= L2CAP_LM_AUTH; if (link->hl_flags & HCI_LINK_ENCRYPT) mode |= L2CAP_LM_ENCRYPT; if (link->hl_flags & HCI_LINK_SECURE) mode |= L2CAP_LM_SECURE; /* * The link state will only be OPEN here if the mode change * was successful. So, we can proceed with L2CAP connections, * or notify already establshed channels, to allow any that * are dissatisfied to disconnect before we restart. */ next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link != link) continue; switch(chan->lc_state) { case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_close(chan, ECONNABORTED); break; } chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; err = l2cap_send_connect_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_send_connect_rsp(link, chan->lc_ident, 0, chan->lc_rcid, L2CAP_SECURITY_BLOCK); l2cap_close(chan, ECONNABORTED); break; } l2cap_send_connect_rsp(link, chan->lc_ident, chan->lc_lcid, chan->lc_rcid, L2CAP_SUCCESS); chan->lc_state = L2CAP_WAIT_CONFIG; chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); err = l2cap_send_config_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_RECV_CONNECT_RSP: case L2CAP_WAIT_CONFIG: case L2CAP_OPEN: /* already established */ (*chan->lc_proto->linkmode)(chan->lc_upper, mode); break; default: break; } } link->hl_state = HCI_LINK_OPEN; hci_acl_start(link); } /* d251 2 a252 2 KASSERT(m != NULL); KASSERT(unit != NULL); d366 2 a367 2 KASSERT(link != NULL); KASSERT(m != NULL); a427 4 * This is called when the queue may need restarting: as new data * is queued, after link mode changes have completed, or when device * buffers have cleared. * d431 3 d444 1 a444 1 KASSERT(link != NULL); d447 1 a447 1 KASSERT(unit != NULL); d655 2 a656 2 KASSERT(m != NULL); KASSERT(unit != NULL); d720 1 a720 1 KASSERT(unit != NULL); d730 1 a730 1 callout_init(&link->hl_expire, 0); d754 1 a754 1 KASSERT(link != NULL); d838 2 a839 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); d860 1 a860 1 KASSERT(unit != NULL); @ 1.9.2.1 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $"); d75 2 a76 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); d151 1 a151 1 KASSERT(link != NULL); d251 2 a252 2 KASSERT(m != NULL); KASSERT(unit != NULL); d366 2 a367 2 KASSERT(link != NULL); KASSERT(m != NULL); d444 1 a444 1 KASSERT(link != NULL); d447 1 a447 1 KASSERT(unit != NULL); d655 2 a656 2 KASSERT(m != NULL); KASSERT(unit != NULL); d720 1 a720 1 KASSERT(unit != NULL); d754 1 a754 1 KASSERT(link != NULL); d838 2 a839 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); d860 1 a860 1 KASSERT(unit != NULL); @ 1.9.2.2 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9.2.1 2007/04/15 16:03:58 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9.2.1 2007/04/15 16:03:58 yamt Exp $"); a116 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a213 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a237 162 * Initiate any Link Mode change requests. */ int hci_acl_setmode(struct hci_link *link) { int err; KASSERT(link != NULL); KASSERT(link->hl_unit != NULL); if (link->hl_state != HCI_LINK_OPEN) return EINPROGRESS; if ((link->hl_flags & HCI_LINK_AUTH_REQ) && !(link->hl_flags & HCI_LINK_AUTH)) { hci_auth_req_cp cp; DPRINTF("requesting auth for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_AUTH; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) && !(link->hl_flags & HCI_LINK_ENCRYPT)) { hci_set_con_encryption_cp cp; /* XXX we should check features for encryption capability */ DPRINTF("requesting encryption for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_ENCRYPT; cp.con_handle = htole16(link->hl_handle); cp.encryption_enable = 0x01; err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { hci_change_con_link_key_cp cp; /* always change link key for SECURE requests */ link->hl_flags &= ~HCI_LINK_SECURE; DPRINTF("changing link key for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_SECURE; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } return 0; } /* * Link Mode changed. * * This is called from event handlers when the mode change * is complete. We notify upstream and restart the link. */ void hci_acl_linkmode(struct hci_link *link) { struct l2cap_channel *chan, *next; int err, mode = 0; DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", link->hl_handle, (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); if (link->hl_flags & HCI_LINK_AUTH) mode |= L2CAP_LM_AUTH; if (link->hl_flags & HCI_LINK_ENCRYPT) mode |= L2CAP_LM_ENCRYPT; if (link->hl_flags & HCI_LINK_SECURE) mode |= L2CAP_LM_SECURE; /* * The link state will only be OPEN here if the mode change * was successful. So, we can proceed with L2CAP connections, * or notify already establshed channels, to allow any that * are dissatisfied to disconnect before we restart. */ next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link != link) continue; switch(chan->lc_state) { case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_close(chan, ECONNABORTED); break; } chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; err = l2cap_send_connect_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_send_connect_rsp(link, chan->lc_ident, 0, chan->lc_rcid, L2CAP_SECURITY_BLOCK); l2cap_close(chan, ECONNABORTED); break; } l2cap_send_connect_rsp(link, chan->lc_ident, chan->lc_lcid, chan->lc_rcid, L2CAP_SUCCESS); chan->lc_state = L2CAP_WAIT_CONFIG; chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); err = l2cap_send_config_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_RECV_CONNECT_RSP: case L2CAP_WAIT_CONFIG: case L2CAP_OPEN: /* already established */ (*chan->lc_proto->linkmode)(chan->lc_upper, mode); break; default: break; } } link->hl_state = HCI_LINK_OPEN; hci_acl_start(link); } /* a427 4 * This is called when the queue may need restarting: as new data * is queued, after link mode changes have completed, or when device * buffers have cleared. * d431 3 @ 1.9.6.1 log @Sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $"); d75 2 a76 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); d151 1 a151 1 KASSERT(link != NULL); d251 2 a252 2 KASSERT(m != NULL); KASSERT(unit != NULL); d366 2 a367 2 KASSERT(link != NULL); KASSERT(m != NULL); d444 1 a444 1 KASSERT(link != NULL); d447 1 a447 1 KASSERT(unit != NULL); d655 2 a656 2 KASSERT(m != NULL); KASSERT(unit != NULL); d720 1 a720 1 KASSERT(unit != NULL); d754 1 a754 1 KASSERT(link != NULL); d838 2 a839 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); d860 1 a860 1 KASSERT(unit != NULL); @ 1.9.6.2 log @Sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9.6.1 2007/04/10 13:26:48 ad Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9.6.1 2007/04/10 13:26:48 ad Exp $"); a116 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a213 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a237 162 * Initiate any Link Mode change requests. */ int hci_acl_setmode(struct hci_link *link) { int err; KASSERT(link != NULL); KASSERT(link->hl_unit != NULL); if (link->hl_state != HCI_LINK_OPEN) return EINPROGRESS; if ((link->hl_flags & HCI_LINK_AUTH_REQ) && !(link->hl_flags & HCI_LINK_AUTH)) { hci_auth_req_cp cp; DPRINTF("requesting auth for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_AUTH; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) && !(link->hl_flags & HCI_LINK_ENCRYPT)) { hci_set_con_encryption_cp cp; /* XXX we should check features for encryption capability */ DPRINTF("requesting encryption for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_ENCRYPT; cp.con_handle = htole16(link->hl_handle); cp.encryption_enable = 0x01; err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { hci_change_con_link_key_cp cp; /* always change link key for SECURE requests */ link->hl_flags &= ~HCI_LINK_SECURE; DPRINTF("changing link key for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_SECURE; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } return 0; } /* * Link Mode changed. * * This is called from event handlers when the mode change * is complete. We notify upstream and restart the link. */ void hci_acl_linkmode(struct hci_link *link) { struct l2cap_channel *chan, *next; int err, mode = 0; DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", link->hl_handle, (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); if (link->hl_flags & HCI_LINK_AUTH) mode |= L2CAP_LM_AUTH; if (link->hl_flags & HCI_LINK_ENCRYPT) mode |= L2CAP_LM_ENCRYPT; if (link->hl_flags & HCI_LINK_SECURE) mode |= L2CAP_LM_SECURE; /* * The link state will only be OPEN here if the mode change * was successful. So, we can proceed with L2CAP connections, * or notify already establshed channels, to allow any that * are dissatisfied to disconnect before we restart. */ next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link != link) continue; switch(chan->lc_state) { case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_close(chan, ECONNABORTED); break; } chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; err = l2cap_send_connect_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_send_connect_rsp(link, chan->lc_ident, 0, chan->lc_rcid, L2CAP_SECURITY_BLOCK); l2cap_close(chan, ECONNABORTED); break; } l2cap_send_connect_rsp(link, chan->lc_ident, chan->lc_lcid, chan->lc_rcid, L2CAP_SUCCESS); chan->lc_state = L2CAP_WAIT_CONFIG; chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); err = l2cap_send_config_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_RECV_CONNECT_RSP: case L2CAP_WAIT_CONFIG: case L2CAP_OPEN: /* already established */ (*chan->lc_proto->linkmode)(chan->lc_upper, mode); break; default: break; } } link->hl_state = HCI_LINK_OPEN; hci_acl_start(link); } /* a427 4 * This is called when the queue may need restarting: as new data * is queued, after link mode changes have completed, or when device * buffers have cleared. * d431 3 @ 1.9.6.3 log @Adapt to callout API change. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9.6.2 2007/06/08 14:17:40 ad Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9.6.2 2007/06/08 14:17:40 ad Exp $"); d899 1 a899 1 callout_init(&link->hl_expire, 0); @ 1.9.6.4 log @Sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9.6.3 2007/07/01 21:50:47 ad Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9.6.3 2007/07/01 21:50:47 ad Exp $"); d99 3 a101 3 cp.page_scan_rep_mode = memo->page_scan_rep_mode; cp.page_scan_mode = memo->page_scan_mode; cp.clock_offset = memo->clock_offset; a992 12 /* * If we made a note of clock offset, keep it in a memo * to facilitate reconnections to this device */ if (link->hl_clock != 0) { struct hci_memo *memo; memo = hci_memo_new(link->hl_unit, &link->hl_bdaddr); if (memo != NULL) memo->clock_offset = link->hl_clock; } @ 1.8 log @__unused removal on arguments; approved by core. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.7 2006/10/12 01:32:37 christos Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.7 2006/10/12 01:32:37 christos Exp $"); a416 1 if (n) m_freem(n); @ 1.8.4.1 log @Sync w/ NetBSD-4-RC_1 @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.8.2.1 2007/07/19 16:04:18 liamjfoy Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.8.2.1 2007/07/19 16:04:18 liamjfoy Exp $"); a116 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a213 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a237 162 * Initiate any Link Mode change requests. */ int hci_acl_setmode(struct hci_link *link) { int err; KASSERT(link != NULL); KASSERT(link->hl_unit != NULL); if (link->hl_state != HCI_LINK_OPEN) return EINPROGRESS; if ((link->hl_flags & HCI_LINK_AUTH_REQ) && !(link->hl_flags & HCI_LINK_AUTH)) { hci_auth_req_cp cp; DPRINTF("requesting auth for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_AUTH; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) && !(link->hl_flags & HCI_LINK_ENCRYPT)) { hci_set_con_encryption_cp cp; /* XXX we should check features for encryption capability */ DPRINTF("requesting encryption for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_ENCRYPT; cp.con_handle = htole16(link->hl_handle); cp.encryption_enable = 0x01; err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { hci_change_con_link_key_cp cp; /* always change link key for SECURE requests */ link->hl_flags &= ~HCI_LINK_SECURE; DPRINTF("changing link key for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_SECURE; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } return 0; } /* * Link Mode changed. * * This is called from event handlers when the mode change * is complete. We notify upstream and restart the link. */ void hci_acl_linkmode(struct hci_link *link) { struct l2cap_channel *chan, *next; int err, mode = 0; DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", link->hl_handle, (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); if (link->hl_flags & HCI_LINK_AUTH) mode |= L2CAP_LM_AUTH; if (link->hl_flags & HCI_LINK_ENCRYPT) mode |= L2CAP_LM_ENCRYPT; if (link->hl_flags & HCI_LINK_SECURE) mode |= L2CAP_LM_SECURE; /* * The link state will only be OPEN here if the mode change * was successful. So, we can proceed with L2CAP connections, * or notify already establshed channels, to allow any that * are dissatisfied to disconnect before we restart. */ next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link != link) continue; switch(chan->lc_state) { case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_close(chan, ECONNABORTED); break; } chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; err = l2cap_send_connect_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_send_connect_rsp(link, chan->lc_ident, 0, chan->lc_rcid, L2CAP_SECURITY_BLOCK); l2cap_close(chan, ECONNABORTED); break; } l2cap_send_connect_rsp(link, chan->lc_ident, chan->lc_lcid, chan->lc_rcid, L2CAP_SUCCESS); chan->lc_state = L2CAP_WAIT_CONFIG; chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); err = l2cap_send_config_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_RECV_CONNECT_RSP: case L2CAP_WAIT_CONFIG: case L2CAP_OPEN: /* already established */ (*chan->lc_proto->linkmode)(chan->lc_upper, mode); break; default: break; } } link->hl_state = HCI_LINK_OPEN; hci_acl_start(link); } /* a428 4 * This is called when the queue may need restarting: as new data * is queued, after link mode changes have completed, or when device * buffers have cleared. * d432 3 @ 1.8.2.1 log @Pull up following revision(s) (requested by plunky in ticket #744): sys/netbt/l2cap_lower.c: revision 1.6 sys/dev/bluetooth/btdev.h: revision 1.6 sys/netbt/sco_socket.c: revision 1.9 sys/netbt/rfcomm_upper.c: revision 1.3 sys/netbt/l2cap_socket.c: revision 1.7 sys/netbt/rfcomm_upper.c: revision 1.5 lib/libusbhid/usbhid.h: revision 1.5 sys/netbt/rfcomm_upper.c: revision 1.6 usr.sbin/btdevctl/btdevctl.c: revision 1.4 usr.sbin/btdevctl/btdevctl.h: revision 1.3 usr.sbin/btdevctl/btdevctl.8: revision 1.4 sys/netbt/rfcomm_session.c: revision 1.5 sys/netbt/hci.h: revision 1.10 usr.bin/rfcomm_sppd/rfcomm_sppd.c: revision 1.6 sys/netbt/hci_link.c: revision 1.11 usr.bin/rfcomm_sppd/rfcomm_sppd.c: revision 1.7 usr.bin/rfcomm_sppd/rfcomm_sppd.c: revision 1.8 sys/dev/bluetooth/btsco.c: revision 1.14 sys/netbt/rfcomm_session.c: revision 1.9 usr.sbin/btdevctl/sdp.c: revision 1.2 share/man/man9/bluetooth.9: revision 1.2 usr.sbin/btdevctl/sdp.c: revision 1.3 sys/dev/bluetooth/bthidev.c: revision 1.8 sys/netbt/l2cap.h: revision 1.4 sys/netbt/rfcomm.h: revision 1.3 sys/netbt/l2cap.h: revision 1.5 sys/netbt/l2cap_misc.c: revision 1.3 share/man/man4/bluetooth.4: revision 1.5 lib/libusbhid/usbhid.3: revision 1.11 sys/netbt/bluetooth.h: revision 1.5 share/man/man4/bthidev.4: revision 1.8 sys/netbt/rfcomm_dlc.c: revision 1.3 usr.sbin/btdevctl/print.c: revision 1.8 sys/netbt/rfcomm_socket.c: revision 1.7 sys/netbt/l2cap_signal.c: revision 1.4 sys/netbt/l2cap_signal.c: revision 1.5 sys/netbt/l2cap_signal.c: revision 1.7 sys/netbt/hci_event.c: revision 1.6 usr.bin/rfcomm_sppd/rfcomm_sppd.1: revision 1.5 sys/netbt/l2cap_upper.c: revision 1.3 sys/netbt/l2cap_lower.c: revision 1.2 usr.sbin/btdevctl/db.c: revision 1.3 sys/netbt/l2cap_upper.c: revision 1.6 lib/libusbhid/descr.c: revision 1.5 sys/netbt/l2cap_upper.c: revision 1.7 sys/netbt/l2cap_lower.c: revision 1.4 Add 'service level' security for L2CAP and RFCOMM connections, following the Linux (BlueZ) API. - L2CAP or RFCOMM connections can require the baseband radio link mode be any of: authenticated (devices are paired) encrypted (implies authentication) secured (encryption, plus generate new link key) - for sockets, the mode is set using setsockopt(2) and the socket connection will be aborted if the mode change fails. - mode settings will be applied during connection establishment, and for safety, we enter a wait state and will only proceed when the mode settings are successfuly set. - It is possible to change the mode on already open connections, but not possible to guarantee that data already queued (from either end) will not be delivered. (this is a feature, not a bug) - bthidev(4) and rfcomm_sppd(1) support "auth", "encrypt" and "secure" options - btdevctl(8) by default enables "auth" for HIDs, and "encrypt" for keyboards (which are required to support it) - ALSO INCLUDES OTHER MINOR FIXES @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); a116 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a213 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a237 162 * Initiate any Link Mode change requests. */ int hci_acl_setmode(struct hci_link *link) { int err; KASSERT(link != NULL); KASSERT(link->hl_unit != NULL); if (link->hl_state != HCI_LINK_OPEN) return EINPROGRESS; if ((link->hl_flags & HCI_LINK_AUTH_REQ) && !(link->hl_flags & HCI_LINK_AUTH)) { hci_auth_req_cp cp; DPRINTF("requesting auth for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_AUTH; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) && !(link->hl_flags & HCI_LINK_ENCRYPT)) { hci_set_con_encryption_cp cp; /* XXX we should check features for encryption capability */ DPRINTF("requesting encryption for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_ENCRYPT; cp.con_handle = htole16(link->hl_handle); cp.encryption_enable = 0x01; err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { hci_change_con_link_key_cp cp; /* always change link key for SECURE requests */ link->hl_flags &= ~HCI_LINK_SECURE; DPRINTF("changing link key for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_SECURE; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } return 0; } /* * Link Mode changed. * * This is called from event handlers when the mode change * is complete. We notify upstream and restart the link. */ void hci_acl_linkmode(struct hci_link *link) { struct l2cap_channel *chan, *next; int err, mode = 0; DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", link->hl_handle, (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); if (link->hl_flags & HCI_LINK_AUTH) mode |= L2CAP_LM_AUTH; if (link->hl_flags & HCI_LINK_ENCRYPT) mode |= L2CAP_LM_ENCRYPT; if (link->hl_flags & HCI_LINK_SECURE) mode |= L2CAP_LM_SECURE; /* * The link state will only be OPEN here if the mode change * was successful. So, we can proceed with L2CAP connections, * or notify already establshed channels, to allow any that * are dissatisfied to disconnect before we restart. */ next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link != link) continue; switch(chan->lc_state) { case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_close(chan, ECONNABORTED); break; } chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; err = l2cap_send_connect_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_send_connect_rsp(link, chan->lc_ident, 0, chan->lc_rcid, L2CAP_SECURITY_BLOCK); l2cap_close(chan, ECONNABORTED); break; } l2cap_send_connect_rsp(link, chan->lc_ident, chan->lc_lcid, chan->lc_rcid, L2CAP_SUCCESS); chan->lc_state = L2CAP_WAIT_CONFIG; chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); err = l2cap_send_config_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_RECV_CONNECT_RSP: case L2CAP_WAIT_CONFIG: case L2CAP_OPEN: /* already established */ (*chan->lc_proto->linkmode)(chan->lc_upper, mode); break; default: break; } } link->hl_state = HCI_LINK_OPEN; hci_acl_start(link); } /* a428 4 * This is called when the queue may need restarting: as new data * is queued, after link mode changes have completed, or when device * buffers have cleared. * d432 3 @ 1.8.2.2 log @Pull up following revision(s) (requested by plunky in ticket #1409): sys/netbt/hci_link.c: revision 1.22 Some Broadcom controllers emit empty ACL packets during connection setup, using the handle that they have not yet told us for the connection-to-be. Disconnecting can cause problems so just ignore zero length ACL packets on unknown connection handles. fixes a problem reported by Nick Hudson @ text @a455 4 * * This can cause a problem with some Broadcom controllers * which emit empty ACL packets during connection setup, so * only disconnect where data is present. d457 3 a459 5 if (hdr.length > 0) { cp.con_handle = htole16(handle); cp.reason = 0x13;/*"Remote User Terminated Connection"*/ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); } @ 1.7 log @- sprinkle __unused on function decls. - fix a couple of unused bugs - no more -Wno-unused for i386 @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.6 2006/10/04 15:49:59 christos Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.6 2006/10/04 15:49:59 christos Exp $"); d694 1 a694 1 hci_sco_start(struct hci_link *link __unused) @ 1.6 log @fix empty if @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.5 2006/09/11 22:12:39 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.5 2006/09/11 22:12:39 plunky Exp $"); d694 1 a694 1 hci_sco_start(struct hci_link *link) @ 1.5 log @Endian issues: hci_event.c: - Convert memo->response.clock_offset to host-endian. hci_ioctl.c: - printf format tweak (size_t) hci_link.c: - Convert memo->response.clock_offset from host-endian. - Tweak a DIAGNOSTIC message. l2cap_signal.c: - In l2cap_recv_config_req(), rp->scid is little-endian so make sure we convert from host-endian. from scw@@ @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.4 2006/09/11 22:08:38 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.4 2006/09/11 22:08:38 plunky Exp $"); d221 1 a221 1 if (err) d223 2 a224 1 err); @ 1.5.2.1 log @sync with head @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.5 2006/09/11 22:12:39 plunky Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.5 2006/09/11 22:12:39 plunky Exp $"); d221 1 a221 1 if (err) { d223 1 a223 2 err); } d693 1 a693 1 hci_sco_start(struct hci_link *link __unused) @ 1.5.2.2 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.5.2.1 2006/10/22 06:07:28 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.5.2.1 2006/10/22 06:07:28 yamt Exp $"); d694 1 a694 1 hci_sco_start(struct hci_link *link) @ 1.4 log @hci_link.c: - In hci_link_free(), do not unlink items from a LIST queue within a LIST_FOREACH() iterator. rfcomm_session.c: - In rfcomm_session_recv_mcc_nsc(), do not unlink items from a LIST queue within a LIST_FOREACH() iterator. from scw@@ @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.3 2006/07/26 10:20:56 tron Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.3 2006/07/26 10:20:56 tron Exp $"); d101 1 a101 1 cp.clock_offset = memo->response.clock_offset; d264 2 a265 1 printf("%s: bad ACL packet length\n", unit->hci_devname); @ 1.3 log @Bluetooth fixes by Iain Hibbert: - Enable listening and incoming connections for SCO links. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.2 2006/07/26 10:10:06 tron Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.2 2006/07/26 10:10:06 tron Exp $"); d751 1 a751 1 struct l2cap_channel *chan; d761 3 a763 1 LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { @ 1.3.6.1 log @file hci_link.c was added on branch rpaulo-netinet-merge-pcb on 2006-09-09 02:58:38 +0000 @ text @d1 865 @ 1.3.6.2 log @sync with head @ text @a0 865 /* $NetBSD: hci_link.c,v 1.3.6.1 2006/09/09 02:58:38 rpaulo Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. * Copyright (c) 2006 Itronix Inc. * 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. * 3. The name of Itronix Inc. may not be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. */ #include __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.3.6.1 2006/09/09 02:58:38 rpaulo Exp $"); #include #include #include #include #include #include #include #include #include #include #include /******************************************************************************* * * HCI ACL Connections */ /* * Automatically expire unused ACL connections after this number of * seconds (if zero, do not expire unused connections) [sysctl] */ int hci_acl_expiry = 10; /* seconds */ /* * hci_acl_open(unit, bdaddr) * * open ACL connection to remote bdaddr. Only one ACL connection is permitted * between any two Bluetooth devices, so we look for an existing one before * trying to start a new one. */ struct hci_link * hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; struct hci_memo *memo; hci_create_con_cp cp; int err; KASSERT(unit); KASSERT(bdaddr); link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link == NULL) { link = hci_link_alloc(unit); if (link == NULL) return NULL; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); } switch(link->hl_state) { case HCI_LINK_CLOSED: /* * open connection to remote device */ memset(&cp, 0, sizeof(cp)); bdaddr_copy(&cp.bdaddr, bdaddr); cp.pkt_type = htole16(unit->hci_packet_type); memo = hci_memo_find(unit, bdaddr); if (memo != NULL) { cp.page_scan_rep_mode = memo->response.page_scan_rep_mode; cp.page_scan_mode = memo->response.page_scan_mode; cp.clock_offset = memo->response.clock_offset; } if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH) cp.accept_role_switch = 1; err = hci_send_cmd(unit, HCI_CMD_CREATE_CON, &cp, sizeof(cp)); if (err) { hci_link_free(link, err); return NULL; } link->hl_state = HCI_LINK_WAIT_CONNECT; break; case HCI_LINK_WAIT_CONNECT: /* * somebody else already trying to connect, we just * sit on the bench with them.. */ break; case HCI_LINK_OPEN: /* * If already open, halt any expiry timeouts. We dont need * to care about already invoking timeouts since refcnt >0 * will keep the link alive. */ callout_stop(&link->hl_expire); break; default: UNKNOWN(link->hl_state); return NULL; } /* open */ link->hl_refcnt++; return link; } /* * Close ACL connection. When there are no more references to this link, * we can either close it down or schedule a delayed closedown. */ void hci_acl_close(struct hci_link *link, int err) { KASSERT(link); if (--link->hl_refcnt == 0) { if (link->hl_state == HCI_LINK_CLOSED) hci_link_free(link, err); else if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } } /* * Incoming ACL connection. * * For now, we accept all connections but it would be better to check * the L2CAP listen list and only accept when there is a listener * available. * * There should not be a link to the same bdaddr already, we check * anyway though its left unhandled for now. */ struct hci_link * hci_acl_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link != NULL) return NULL; link = hci_link_alloc(unit); if (link != NULL) { link->hl_state = HCI_LINK_WAIT_CONNECT; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } return link; } void hci_acl_timeout(void *arg) { struct hci_link *link = arg; hci_discon_cp cp; int s, err; s = splsoftnet(); callout_ack(&link->hl_expire); if (link->hl_refcnt > 0) goto out; DPRINTF("link #%d expired\n", link->hl_handle); switch (link->hl_state) { case HCI_LINK_CLOSED: case HCI_LINK_WAIT_CONNECT: hci_link_free(link, ECONNRESET); break; case HCI_LINK_OPEN: cp.con_handle = htole16(link->hl_handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ err = hci_send_cmd(link->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); if (err) DPRINTF("error %d sending HCI_CMD_DISCONNECT\n", err); break; default: UNKNOWN(link->hl_state); break; } out: splx(s); } /* * Receive ACL Data * * we accumulate packet fragments on the hci_link structure * until a full L2CAP frame is ready, then send it on. */ void hci_acl_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_acldata_hdr_t hdr; uint16_t handle, want; int pb, got; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_ACL_DATA_PKT) { printf("%s: bad ACL packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != le16toh(hdr.length)) { printf("%s: bad ACL packet length\n", unit->hci_devname); goto bad; } #endif hdr.length = le16toh(hdr.length); hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); pb = HCI_PB_FLAG(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL) { hci_discon_cp cp; DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); /* * There is no way to find out what this connection handle is * for, just get rid of it. This may happen, if a USB dongle * is plugged into a self powered hub and does not reset when * the system is shut down. */ cp.con_handle = htole16(handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); goto bad; } switch (pb) { case HCI_PACKET_START: if (link->hl_rxp != NULL) printf("%s: dropped incomplete ACL packet\n", unit->hci_devname); if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) { printf("%s: short ACL packet\n", unit->hci_devname); goto bad; } link->hl_rxp = m; got = m->m_pkthdr.len; break; case HCI_PACKET_FRAGMENT: if (link->hl_rxp == NULL) { printf("%s: unexpected packet fragment\n", unit->hci_devname); goto bad; } got = m->m_pkthdr.len + link->hl_rxp->m_pkthdr.len; m_cat(link->hl_rxp, m); m = link->hl_rxp; m->m_pkthdr.len = got; break; default: printf("%s: unknown packet type\n", unit->hci_devname); goto bad; } m_copydata(m, 0, sizeof(want), &want); want = le16toh(want) + sizeof(l2cap_hdr_t) - got; if (want > 0) return; link->hl_rxp = NULL; if (want == 0) { l2cap_recv_frame(m, link); return; } bad: m_freem(m); } /* * Send ACL data on link * * We must fragment packets into chunks of less than unit->hci_max_acl_size and * prepend a relevant ACL header to each fragment. We keep a PDU structure * attached to the link, so that completed fragments can be marked off and * more data requested from above once the PDU is sent. */ int hci_acl_send(struct mbuf *m, struct hci_link *link, struct l2cap_channel *chan) { struct l2cap_pdu *pdu; struct mbuf *n = NULL; int plen, mlen, num = 0; KASSERT(link); KASSERT(m); KASSERT(m->m_flags & M_PKTHDR); KASSERT(m->m_pkthdr.len > 0); if (link->hl_state == HCI_LINK_CLOSED) { m_freem(m); return ENETDOWN; } pdu = pool_get(&l2cap_pdu_pool, PR_NOWAIT); if (pdu == NULL) goto nomem; pdu->lp_chan = chan; pdu->lp_pending = 0; MBUFQ_INIT(&pdu->lp_data); plen = m->m_pkthdr.len; mlen = link->hl_unit->hci_max_acl_size; DPRINTFN(5, "%s: handle #%d, plen = %d, max = %d\n", link->hl_unit->hci_devname, link->hl_handle, plen, mlen); while (plen > 0) { if (plen > mlen) { n = m_split(m, mlen, M_DONTWAIT); if (n == NULL) goto nomem; } else { mlen = plen; } if (num++ == 0) m->m_flags |= M_PROTO1; /* tag first fragment */ DPRINTFN(10, "chunk of %d (plen = %d) bytes\n", mlen, plen); MBUFQ_ENQUEUE(&pdu->lp_data, m); m = n; plen -= mlen; } TAILQ_INSERT_TAIL(&link->hl_txq, pdu, lp_next); link->hl_txqlen += num; hci_acl_start(link); return 0; nomem: if (m) m_freem(m); if (n) m_freem(n); if (pdu) { MBUFQ_DRAIN(&pdu->lp_data); pool_put(&l2cap_pdu_pool, pdu); } return ENOMEM; } /* * Start sending ACL data on link. * * We may use all the available packet slots. The reason that we add * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP * signal packets may be queued before the handle is given to us.. * * this is called from hci_acl_send() above, and the event processing * code (for CON_COMPL and NUM_COMPL_PKTS) */ void hci_acl_start(struct hci_link *link) { struct hci_unit *unit; hci_acldata_hdr_t *hdr; struct l2cap_pdu *pdu; struct mbuf *m; uint16_t handle; KASSERT(link); unit = link->hl_unit; KASSERT(unit); /* this is mainly to block ourselves (below) */ if (link->hl_state != HCI_LINK_OPEN) return; if (link->hl_txqlen == 0 || unit->hci_num_acl_pkts == 0) return; /* find first PDU with data to send */ pdu = TAILQ_FIRST(&link->hl_txq); for (;;) { if (pdu == NULL) return; if (MBUFQ_FIRST(&pdu->lp_data) != NULL) break; pdu = TAILQ_NEXT(pdu, lp_next); } while (unit->hci_num_acl_pkts > 0) { MBUFQ_DEQUEUE(&pdu->lp_data, m); KASSERT(m != NULL); if (m->m_flags & M_PROTO1) handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_START, 0); else handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_FRAGMENT, 0); M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) break; hdr = mtod(m, hci_acldata_hdr_t *); hdr->type = HCI_ACL_DATA_PKT; hdr->con_handle = htole16(handle); hdr->length = htole16(m->m_pkthdr.len - sizeof(*hdr)); link->hl_txqlen--; pdu->lp_pending++; hci_output_acl(unit, m); if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { if (pdu->lp_chan) { /* * This should enable streaming of PDUs - when * we have placed all the fragments on the acl * output queue, we trigger the L2CAP layer to * send us down one more. Use a false state so * we dont run into ourselves coming back from * the future.. */ link->hl_state = HCI_LINK_BLOCK; l2cap_start(pdu->lp_chan); link->hl_state = HCI_LINK_OPEN; } pdu = TAILQ_NEXT(pdu, lp_next); if (pdu == NULL) break; } } /* * We had our turn now, move to the back of the queue to let * other links have a go at the output buffers.. */ if (TAILQ_NEXT(link, hl_next)) { TAILQ_REMOVE(&unit->hci_links, link, hl_next); TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); } } /* * Confirm ACL packets cleared from Controller buffers. We scan our PDU * list to clear pending fragments and signal upstream for more data * when a PDU is complete. */ void hci_acl_complete(struct hci_link *link, int num) { struct l2cap_pdu *pdu; struct l2cap_channel *chan; DPRINTFN(5, "handle #%d (%d)\n", link->hl_handle, num); while (num > 0) { pdu = TAILQ_FIRST(&link->hl_txq); if (pdu == NULL) { printf("%s: %d packets completed on handle #%x " "but none pending!\n", link->hl_unit->hci_devname, num, link->hl_handle); return; } if (num >= pdu->lp_pending) { num -= pdu->lp_pending; pdu->lp_pending = 0; if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); chan = pdu->lp_chan; if (chan != NULL) { chan->lc_pending--; (*chan->lc_proto->complete) (chan->lc_upper, 1); if (chan->lc_pending == 0) l2cap_start(chan); } pool_put(&l2cap_pdu_pool, pdu); } } else { pdu->lp_pending -= num; num = 0; } } } /******************************************************************************* * * HCI SCO Connections */ /* * Incoming SCO Connection. We check the list for anybody willing * to take it. */ struct hci_link * hci_sco_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct sockaddr_bt laddr, raddr; struct sco_pcb *pcb, *new; struct hci_link *sco, *acl; memset(&laddr, 0, sizeof(laddr)); laddr.bt_len = sizeof(laddr); laddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&laddr.bt_bdaddr, &unit->hci_bdaddr); memset(&raddr, 0, sizeof(raddr)); raddr.bt_len = sizeof(raddr); raddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&raddr.bt_bdaddr, bdaddr); /* * There should already be an ACL link up and running before * the controller sends us SCO connection requests, but you * never know.. */ acl = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (acl == NULL || acl->hl_state != HCI_LINK_OPEN) return NULL; LIST_FOREACH(pcb, &sco_pcb, sp_next) { if ((pcb->sp_flags & SP_LISTENING) == 0) continue; new = (*pcb->sp_proto->newconn)(pcb->sp_upper, &laddr, &raddr); if (new == NULL) continue; /* * Ok, got new pcb so we can start a new link and fill * in all the details. */ bdaddr_copy(&new->sp_laddr, &unit->hci_bdaddr); bdaddr_copy(&new->sp_raddr, bdaddr); sco = hci_link_alloc(unit); if (sco == NULL) { sco_detach(&new); return NULL; } sco->hl_type = HCI_LINK_SCO; bdaddr_copy(&sco->hl_bdaddr, bdaddr); sco->hl_link = hci_acl_open(unit, bdaddr); KASSERT(sco->hl_link == acl); sco->hl_sco = new; new->sp_link = sco; new->sp_mtu = unit->hci_max_sco_size; return sco; } return NULL; } /* * receive SCO packet, we only need to strip the header and send * it to the right handler */ void hci_sco_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_scodata_hdr_t hdr; uint16_t handle; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_SCO_DATA_PKT) { printf("%s: bad SCO packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != hdr.length) { printf("%s: bad SCO packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, hdr.length); goto bad; } #endif hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL || link->hl_type == HCI_LINK_ACL) { DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); goto bad; } (*link->hl_sco->sp_proto->input)(link->hl_sco->sp_upper, m); return; bad: m_freem(m); } void hci_sco_start(struct hci_link *link) { } /* * SCO packets have completed at the controller, so we can * signal up to free the buffer space. */ void hci_sco_complete(struct hci_link *link, int num) { DPRINTFN(5, "handle #%d (num=%d)\n", link->hl_handle, num); link->hl_sco->sp_pending--; (*link->hl_sco->sp_proto->complete)(link->hl_sco->sp_upper, num); } /******************************************************************************* * * Generic HCI Connection alloc/free/lookup etc */ struct hci_link * hci_link_alloc(struct hci_unit *unit) { struct hci_link *link; KASSERT(unit); link = malloc(sizeof(struct hci_link), M_BLUETOOTH, M_NOWAIT | M_ZERO); if (link == NULL) return NULL; link->hl_unit = unit; link->hl_state = HCI_LINK_CLOSED; /* init ACL portion */ callout_init(&link->hl_expire); callout_setfunc(&link->hl_expire, hci_acl_timeout, link); TAILQ_INIT(&link->hl_txq); /* outgoing packets */ TAILQ_INIT(&link->hl_reqs); /* request queue */ link->hl_mtu = L2CAP_MTU_DEFAULT; /* L2CAP signal mtu */ link->hl_flush = L2CAP_FLUSH_TIMO_DEFAULT; /* flush timeout */ /* init SCO portion */ MBUFQ_INIT(&link->hl_data); /* attach to unit */ TAILQ_INSERT_HEAD(&unit->hci_links, link, hl_next); return link; } void hci_link_free(struct hci_link *link, int err) { struct l2cap_req *req; struct l2cap_pdu *pdu; struct l2cap_channel *chan; KASSERT(link); DPRINTF("#%d, type = %d, state = %d, refcnt = %d\n", link->hl_handle, link->hl_type, link->hl_state, link->hl_refcnt); /* ACL reference count */ if (link->hl_refcnt > 0) { LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { if (chan->lc_link == link) l2cap_close(chan, err); } } KASSERT(link->hl_refcnt == 0); /* ACL L2CAP requests.. */ while ((req = TAILQ_FIRST(&link->hl_reqs)) != NULL) l2cap_request_free(req); KASSERT(TAILQ_EMPTY(&link->hl_reqs)); /* ACL outgoing data queue */ while ((pdu = TAILQ_FIRST(&link->hl_txq)) != NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); MBUFQ_DRAIN(&pdu->lp_data); if (pdu->lp_pending) link->hl_unit->hci_num_acl_pkts += pdu->lp_pending; pool_put(&l2cap_pdu_pool, pdu); } KASSERT(TAILQ_EMPTY(&link->hl_txq)); /* ACL incoming data packet */ if (link->hl_rxp != NULL) { m_freem(link->hl_rxp); link->hl_rxp = NULL; } /* SCO master ACL link */ if (link->hl_link != NULL) { hci_acl_close(link->hl_link, err); link->hl_link = NULL; } /* SCO pcb */ if (link->hl_sco != NULL) { struct sco_pcb *pcb; pcb = link->hl_sco; pcb->sp_link = NULL; link->hl_sco = NULL; (*pcb->sp_proto->disconnected)(pcb->sp_upper, err); } /* flush any SCO data */ MBUFQ_DRAIN(&link->hl_data); /* * Halt the callout - if its already running we cannot free the * link structure but the timeout function will call us back in * any case. */ link->hl_state = HCI_LINK_CLOSED; callout_stop(&link->hl_expire); if (callout_invoking(&link->hl_expire)) return; TAILQ_REMOVE(&link->hl_unit->hci_links, link, hl_next); free(link, M_BLUETOOTH); } /* * Lookup HCI link by address and type. Note that for SCO links there may * be more than one link per address, so we only return links with no * handle (ie new links) */ struct hci_link * hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint16_t type) { struct hci_link *link; KASSERT(unit); KASSERT(bdaddr); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (link->hl_type != type) continue; if (type == HCI_LINK_SCO && link->hl_handle != 0) continue; if (bdaddr_same(&link->hl_bdaddr, bdaddr)) break; } return link; } struct hci_link * hci_link_lookup_handle(struct hci_unit *unit, uint16_t handle) { struct hci_link *link; KASSERT(unit); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (handle == link->hl_handle) break; } return link; } @ 1.3.4.1 log @Sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.8 2006/11/16 01:33:45 christos Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.8 2006/11/16 01:33:45 christos Exp $"); d101 1 a101 1 cp.clock_offset = htole16(memo->response.clock_offset); d221 1 a221 1 if (err) { d223 1 a223 2 err); } d264 1 a264 2 printf("%s: bad ACL packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, le16toh(hdr.length)); d751 1 a751 1 struct l2cap_channel *chan, *next; d761 1 a761 3 next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); @ 1.3.4.2 log @Sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.9 2006/12/26 00:00:22 alc Exp $"); d417 1 @ 1.3.2.1 log @Pull up following revision(s) (requested by plunky in ticket #161): sys/dev/bluetooth/btdev.h: revision 1.4 distrib/sets/lists/comp/mi: revision 1.922 usr.sbin/postinstall/postinstall: revision 1.25 sys/netbt/hci_unit.c: revision 1.3 sys/netbt/hci_ioctl.c: revision 1.4 usr.sbin/sdpd/profile.c: revision 1.2 usr.sbin/btdevctl/btdevctl.c: revision 1.2 share/man/man4/Makefile: revision 1.405 distrib/sets/lists/man/mi: revision 1.930 distrib/sets/lists/etc/mi: revision 1.176 usr.sbin/sdpd/profile.c: revision 1.3 usr.sbin/btdevctl/btdevctl.c: revision 1.3 etc/MAKEDEV.tmpl: revision 1.62 distrib/sets/lists/base/mi: revision 1.650 usr.sbin/btdevctl/btdevctl.h: revision 1.2 usr.bin/sdpquery/sdpquery.1: revision 1.4 sys/netbt/rfcomm_session.c: revision 1.2 usr.sbin/btdevctl/btdevctl.8: revision 1.3 usr.bin/sdpquery/search.c: revision 1.2 usr.sbin/sdpd/Makefile: revision 1.2 sys/dev/bluetooth/Makefile: revision 1.3 usr.sbin/btdevctl/cfg.c: file removal sys/netbt/files.netbt: revision 1.4 usr.sbin/btdevctl/sdp.c: revision 1.1 sys/dev/bluetooth/bthidev.c: revision 1.3 etc/bluetooth/Makefile: revision 1.3 sys/dev/pcmcia/files.pcmcia: revision 1.51 sys/dev/bluetooth/bthidev.c: revision 1.4 sys/dev/bluetooth/bthidev.h: revision 1.3 usr.sbin/btdevctl/dev.c: file removal sys/dev/bluetooth/files.bluetooth: revision 1.10 sys/arch/i386/conf/GENERIC: revision 1.777 share/man/man4/ubt.4: revision 1.6 share/man/man4/bthub.4: revision 1.3 sys/netbt/hci.h: revision 1.5 sys/arch/i386/conf/GENERIC_LAPTOP: revision 1.202 lib/libsdp/sdp.h: revision 1.2 usr.sbin/btdevctl/print.c: revision 1.1 share/man/man4/bthidev.4: revision 1.5 share/man/man4/btdev.4: file removal usr.sbin/btdevctl/print.c: revision 1.2 sys/arch/i386/conf/GENERIC_LAPTOP: revision 1.205 usr.sbin/btdevctl/Makefile: revision 1.2 sys/dev/usb/files.usb: revision 1.70 sys/netbt/l2cap_signal.c: revision 1.2 sys/netbt/hci_link.c: revision 1.4 sys/dev/bluetooth/bthub.c: revision 1.3 share/man/man4/btsco.4: revision 1.5 sys/netbt/hci_link.c: revision 1.5 share/man/man4/btdev.4: revision 1.4 sys/dev/bluetooth/btkbd.c: revision 1.3 sys/dev/bluetooth/btdev.c: file removal sys/netbt/hci_event.c: revision 1.2 sys/dev/bluetooth/btsco.h: revision 1.2 etc/mtree/special: revision 1.101 sys/dev/bluetooth/btsco.c: revision 1.3 sys/conf/majors: revision 1.27 usr.sbin/sdpd/hf.c: revision 1.1 sys/dev/bluetooth/btsco.c: revision 1.4 share/man/man5/rc.conf.5: revision 1.107 sys/dev/bluetooth/btdev.c: revision 1.2 etc/rc.d/btdevctl: revision 1.2 usr.sbin/btdevctl/db.c: revision 1.1 etc/rc.d/btdevctl: revision 1.3 etc/bluetooth/btdevctl.conf: revision 1.1 usr.sbin/btdevctl/hid.c: file removal sys/arch/i386/conf/GENERIC: revision 1.781 sys/dev/bluetooth/btdev.h: revision 1.3 Make btdev default count explicit Fix typo in variable name update to bluetooth device attachment: remove pseudo-device btdev(4) and inherent limitations add bthub(4) which autoconfigures at bluetooth controllers as they are enabled. bluetooth devices now attach here. btdevctl(8) and its cache is updated to handle new semantics etc/rc.d/btdevctl is updated to configure devices from a list in /etc/bluetooth/btdevctl.conf also include service name in dictionary being sent to kernel. (this is not used just yet, but it might be in the future and it will be easier if we dont have to provide code to handle its absence) clarify the CAVEAT section somewhat Add service discovery support for the Handsfree profile Replace static 'FreeBSD' string with operating system name gleaned from uname(3) Halt the callout on detach btsco.c: - sco_getopt(..., SO_SCO_MTU, ...) expects the address of a uint16_t, not an int. So change sc_mtu's type to uint16_t. - Try a little harder to ensure btsco_round_blocksize() does not return zero. Prevents a subsequent panic in audio_init_ringbuffer(). from scw@@ Endian issues: hci_event.c: - Convert memo->response.clock_offset to host-endian. hci_ioctl.c: - printf format tweak (size_t) hci_link.c: - Convert memo->response.clock_offset from host-endian. - Tweak a DIAGNOSTIC message. l2cap_signal.c: - In l2cap_recv_config_req(), rp->scid is little-endian so make sure we convert from host-endian. from scw@@ hci_link.c: - In hci_link_free(), do not unlink items from a LIST queue within a LIST_FOREACH() iterator. rfcomm_session.c: - In rfcomm_session_recv_mcc_nsc(), do not unlink items from a LIST queue within a LIST_FOREACH() iterator. from scw@@ guard against a possible situation where the list of l2cap channels is changed when the bluetooth code is not expecting it to be. During a disconnect, we can detach the channel that is being disconnected, but its not really safe to detach any others. Print explicit 64-bit types using the format macros from int_fmtio.h. Unbreaks the build for our LP64 ports, where "long long" typically is not 64 bits. @ text @d1 1 a1 1 /* $NetBSD$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD$"); d101 1 a101 1 cp.clock_offset = htole16(memo->response.clock_offset); d264 1 a264 2 printf("%s: bad ACL packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, le16toh(hdr.length)); d751 1 a751 1 struct l2cap_channel *chan, *next; d761 1 a761 3 next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); @ 1.2 log @Bluetooth fixes by Iain Hibbert: - Utilise cached inquiry results when making connections. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $"); d162 8 a169 3 * Incoming ACL connection. For now, we accept all connections but it * would be better to check the L2CAP listen list and only accept when * there is a listener available. d176 4 d577 2 a578 1 * Incoming SCO Connection. Not yet implemented d583 56 @ 1.1 log @Initial import of bluetooth stack on behalf of Iain Hibbert. (plunky@@, NetBSD Foundation Membership still pending.) This stack was written by Iain under sponsorship from Itronix Inc. The stack includes support for rfcomm networking (networking via your bluetooth enabled cell phone), hid devices (keyboards/mice), and headsets. Drivers for both PCMCIA and USB bluetooth controllers are included. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c$ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c$"); d46 1 a47 1 #include d71 1 d96 8 @ 1.1.4.1 log @file hci_link.c was added on branch chap-midi on 2006-06-22 03:39:50 +0000 @ text @d1 790 @ 1.1.4.2 log @Complete a sync sys/ with head. @ text @a0 790 /* $NetBSD: hci_link.c,v 1.1.4.1 2006/06/22 03:39:50 chap Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. * Copyright (c) 2006 Itronix Inc. * 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. * 3. The name of Itronix Inc. may not be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. */ #include __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.4.1 2006/06/22 03:39:50 chap Exp $"); #include #include #include #include #include #include #include #include #include #include #include /******************************************************************************* * * HCI ACL Connections */ /* * Automatically expire unused ACL connections after this number of * seconds (if zero, do not expire unused connections) [sysctl] */ int hci_acl_expiry = 10; /* seconds */ /* * hci_acl_open(unit, bdaddr) * * open ACL connection to remote bdaddr. Only one ACL connection is permitted * between any two Bluetooth devices, so we look for an existing one before * trying to start a new one. */ struct hci_link * hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; hci_create_con_cp cp; int err; KASSERT(unit); KASSERT(bdaddr); link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link == NULL) { link = hci_link_alloc(unit); if (link == NULL) return NULL; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); } switch(link->hl_state) { case HCI_LINK_CLOSED: /* * open connection to remote device */ memset(&cp, 0, sizeof(cp)); bdaddr_copy(&cp.bdaddr, bdaddr); cp.pkt_type = htole16(unit->hci_packet_type); if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH) cp.accept_role_switch = 1; err = hci_send_cmd(unit, HCI_CMD_CREATE_CON, &cp, sizeof(cp)); if (err) { hci_link_free(link, err); return NULL; } link->hl_state = HCI_LINK_WAIT_CONNECT; break; case HCI_LINK_WAIT_CONNECT: /* * somebody else already trying to connect, we just * sit on the bench with them.. */ break; case HCI_LINK_OPEN: /* * If already open, halt any expiry timeouts. We dont need * to care about already invoking timeouts since refcnt >0 * will keep the link alive. */ callout_stop(&link->hl_expire); break; default: UNKNOWN(link->hl_state); return NULL; } /* open */ link->hl_refcnt++; return link; } /* * Close ACL connection. When there are no more references to this link, * we can either close it down or schedule a delayed closedown. */ void hci_acl_close(struct hci_link *link, int err) { KASSERT(link); if (--link->hl_refcnt == 0) { if (link->hl_state == HCI_LINK_CLOSED) hci_link_free(link, err); else if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } } /* * Incoming ACL connection. For now, we accept all connections but it * would be better to check the L2CAP listen list and only accept when * there is a listener available. */ struct hci_link * hci_acl_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; link = hci_link_alloc(unit); if (link != NULL) { link->hl_state = HCI_LINK_WAIT_CONNECT; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } return link; } void hci_acl_timeout(void *arg) { struct hci_link *link = arg; hci_discon_cp cp; int s, err; s = splsoftnet(); callout_ack(&link->hl_expire); if (link->hl_refcnt > 0) goto out; DPRINTF("link #%d expired\n", link->hl_handle); switch (link->hl_state) { case HCI_LINK_CLOSED: case HCI_LINK_WAIT_CONNECT: hci_link_free(link, ECONNRESET); break; case HCI_LINK_OPEN: cp.con_handle = htole16(link->hl_handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ err = hci_send_cmd(link->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); if (err) DPRINTF("error %d sending HCI_CMD_DISCONNECT\n", err); break; default: UNKNOWN(link->hl_state); break; } out: splx(s); } /* * Receive ACL Data * * we accumulate packet fragments on the hci_link structure * until a full L2CAP frame is ready, then send it on. */ void hci_acl_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_acldata_hdr_t hdr; uint16_t handle, want; int pb, got; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_ACL_DATA_PKT) { printf("%s: bad ACL packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != le16toh(hdr.length)) { printf("%s: bad ACL packet length\n", unit->hci_devname); goto bad; } #endif hdr.length = le16toh(hdr.length); hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); pb = HCI_PB_FLAG(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL) { hci_discon_cp cp; DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); /* * There is no way to find out what this connection handle is * for, just get rid of it. This may happen, if a USB dongle * is plugged into a self powered hub and does not reset when * the system is shut down. */ cp.con_handle = htole16(handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); goto bad; } switch (pb) { case HCI_PACKET_START: if (link->hl_rxp != NULL) printf("%s: dropped incomplete ACL packet\n", unit->hci_devname); if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) { printf("%s: short ACL packet\n", unit->hci_devname); goto bad; } link->hl_rxp = m; got = m->m_pkthdr.len; break; case HCI_PACKET_FRAGMENT: if (link->hl_rxp == NULL) { printf("%s: unexpected packet fragment\n", unit->hci_devname); goto bad; } got = m->m_pkthdr.len + link->hl_rxp->m_pkthdr.len; m_cat(link->hl_rxp, m); m = link->hl_rxp; m->m_pkthdr.len = got; break; default: printf("%s: unknown packet type\n", unit->hci_devname); goto bad; } m_copydata(m, 0, sizeof(want), &want); want = le16toh(want) + sizeof(l2cap_hdr_t) - got; if (want > 0) return; link->hl_rxp = NULL; if (want == 0) { l2cap_recv_frame(m, link); return; } bad: m_freem(m); } /* * Send ACL data on link * * We must fragment packets into chunks of less than unit->hci_max_acl_size and * prepend a relevant ACL header to each fragment. We keep a PDU structure * attached to the link, so that completed fragments can be marked off and * more data requested from above once the PDU is sent. */ int hci_acl_send(struct mbuf *m, struct hci_link *link, struct l2cap_channel *chan) { struct l2cap_pdu *pdu; struct mbuf *n = NULL; int plen, mlen, num = 0; KASSERT(link); KASSERT(m); KASSERT(m->m_flags & M_PKTHDR); KASSERT(m->m_pkthdr.len > 0); if (link->hl_state == HCI_LINK_CLOSED) { m_freem(m); return ENETDOWN; } pdu = pool_get(&l2cap_pdu_pool, PR_NOWAIT); if (pdu == NULL) goto nomem; pdu->lp_chan = chan; pdu->lp_pending = 0; MBUFQ_INIT(&pdu->lp_data); plen = m->m_pkthdr.len; mlen = link->hl_unit->hci_max_acl_size; DPRINTFN(5, "%s: handle #%d, plen = %d, max = %d\n", link->hl_unit->hci_devname, link->hl_handle, plen, mlen); while (plen > 0) { if (plen > mlen) { n = m_split(m, mlen, M_DONTWAIT); if (n == NULL) goto nomem; } else { mlen = plen; } if (num++ == 0) m->m_flags |= M_PROTO1; /* tag first fragment */ DPRINTFN(10, "chunk of %d (plen = %d) bytes\n", mlen, plen); MBUFQ_ENQUEUE(&pdu->lp_data, m); m = n; plen -= mlen; } TAILQ_INSERT_TAIL(&link->hl_txq, pdu, lp_next); link->hl_txqlen += num; hci_acl_start(link); return 0; nomem: if (m) m_freem(m); if (n) m_freem(n); if (pdu) { MBUFQ_DRAIN(&pdu->lp_data); pool_put(&l2cap_pdu_pool, pdu); } return ENOMEM; } /* * Start sending ACL data on link. * * We may use all the available packet slots. The reason that we add * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP * signal packets may be queued before the handle is given to us.. * * this is called from hci_acl_send() above, and the event processing * code (for CON_COMPL and NUM_COMPL_PKTS) */ void hci_acl_start(struct hci_link *link) { struct hci_unit *unit; hci_acldata_hdr_t *hdr; struct l2cap_pdu *pdu; struct mbuf *m; uint16_t handle; KASSERT(link); unit = link->hl_unit; KASSERT(unit); /* this is mainly to block ourselves (below) */ if (link->hl_state != HCI_LINK_OPEN) return; if (link->hl_txqlen == 0 || unit->hci_num_acl_pkts == 0) return; /* find first PDU with data to send */ pdu = TAILQ_FIRST(&link->hl_txq); for (;;) { if (pdu == NULL) return; if (MBUFQ_FIRST(&pdu->lp_data) != NULL) break; pdu = TAILQ_NEXT(pdu, lp_next); } while (unit->hci_num_acl_pkts > 0) { MBUFQ_DEQUEUE(&pdu->lp_data, m); KASSERT(m != NULL); if (m->m_flags & M_PROTO1) handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_START, 0); else handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_FRAGMENT, 0); M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) break; hdr = mtod(m, hci_acldata_hdr_t *); hdr->type = HCI_ACL_DATA_PKT; hdr->con_handle = htole16(handle); hdr->length = htole16(m->m_pkthdr.len - sizeof(*hdr)); link->hl_txqlen--; pdu->lp_pending++; hci_output_acl(unit, m); if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { if (pdu->lp_chan) { /* * This should enable streaming of PDUs - when * we have placed all the fragments on the acl * output queue, we trigger the L2CAP layer to * send us down one more. Use a false state so * we dont run into ourselves coming back from * the future.. */ link->hl_state = HCI_LINK_BLOCK; l2cap_start(pdu->lp_chan); link->hl_state = HCI_LINK_OPEN; } pdu = TAILQ_NEXT(pdu, lp_next); if (pdu == NULL) break; } } /* * We had our turn now, move to the back of the queue to let * other links have a go at the output buffers.. */ if (TAILQ_NEXT(link, hl_next)) { TAILQ_REMOVE(&unit->hci_links, link, hl_next); TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); } } /* * Confirm ACL packets cleared from Controller buffers. We scan our PDU * list to clear pending fragments and signal upstream for more data * when a PDU is complete. */ void hci_acl_complete(struct hci_link *link, int num) { struct l2cap_pdu *pdu; struct l2cap_channel *chan; DPRINTFN(5, "handle #%d (%d)\n", link->hl_handle, num); while (num > 0) { pdu = TAILQ_FIRST(&link->hl_txq); if (pdu == NULL) { printf("%s: %d packets completed on handle #%x " "but none pending!\n", link->hl_unit->hci_devname, num, link->hl_handle); return; } if (num >= pdu->lp_pending) { num -= pdu->lp_pending; pdu->lp_pending = 0; if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); chan = pdu->lp_chan; if (chan != NULL) { chan->lc_pending--; (*chan->lc_proto->complete) (chan->lc_upper, 1); if (chan->lc_pending == 0) l2cap_start(chan); } pool_put(&l2cap_pdu_pool, pdu); } } else { pdu->lp_pending -= num; num = 0; } } } /******************************************************************************* * * HCI SCO Connections */ /* * Incoming SCO Connection. Not yet implemented */ struct hci_link * hci_sco_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { return NULL; } /* * receive SCO packet, we only need to strip the header and send * it to the right handler */ void hci_sco_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_scodata_hdr_t hdr; uint16_t handle; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_SCO_DATA_PKT) { printf("%s: bad SCO packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != hdr.length) { printf("%s: bad SCO packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, hdr.length); goto bad; } #endif hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL || link->hl_type == HCI_LINK_ACL) { DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); goto bad; } (*link->hl_sco->sp_proto->input)(link->hl_sco->sp_upper, m); return; bad: m_freem(m); } void hci_sco_start(struct hci_link *link) { } /* * SCO packets have completed at the controller, so we can * signal up to free the buffer space. */ void hci_sco_complete(struct hci_link *link, int num) { DPRINTFN(5, "handle #%d (num=%d)\n", link->hl_handle, num); link->hl_sco->sp_pending--; (*link->hl_sco->sp_proto->complete)(link->hl_sco->sp_upper, num); } /******************************************************************************* * * Generic HCI Connection alloc/free/lookup etc */ struct hci_link * hci_link_alloc(struct hci_unit *unit) { struct hci_link *link; KASSERT(unit); link = malloc(sizeof(struct hci_link), M_BLUETOOTH, M_NOWAIT | M_ZERO); if (link == NULL) return NULL; link->hl_unit = unit; link->hl_state = HCI_LINK_CLOSED; /* init ACL portion */ callout_init(&link->hl_expire); callout_setfunc(&link->hl_expire, hci_acl_timeout, link); TAILQ_INIT(&link->hl_txq); /* outgoing packets */ TAILQ_INIT(&link->hl_reqs); /* request queue */ link->hl_mtu = L2CAP_MTU_DEFAULT; /* L2CAP signal mtu */ link->hl_flush = L2CAP_FLUSH_TIMO_DEFAULT; /* flush timeout */ /* init SCO portion */ MBUFQ_INIT(&link->hl_data); /* attach to unit */ TAILQ_INSERT_HEAD(&unit->hci_links, link, hl_next); return link; } void hci_link_free(struct hci_link *link, int err) { struct l2cap_req *req; struct l2cap_pdu *pdu; struct l2cap_channel *chan; KASSERT(link); DPRINTF("#%d, type = %d, state = %d, refcnt = %d\n", link->hl_handle, link->hl_type, link->hl_state, link->hl_refcnt); /* ACL reference count */ if (link->hl_refcnt > 0) { LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { if (chan->lc_link == link) l2cap_close(chan, err); } } KASSERT(link->hl_refcnt == 0); /* ACL L2CAP requests.. */ while ((req = TAILQ_FIRST(&link->hl_reqs)) != NULL) l2cap_request_free(req); KASSERT(TAILQ_EMPTY(&link->hl_reqs)); /* ACL outgoing data queue */ while ((pdu = TAILQ_FIRST(&link->hl_txq)) != NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); MBUFQ_DRAIN(&pdu->lp_data); if (pdu->lp_pending) link->hl_unit->hci_num_acl_pkts += pdu->lp_pending; pool_put(&l2cap_pdu_pool, pdu); } KASSERT(TAILQ_EMPTY(&link->hl_txq)); /* ACL incoming data packet */ if (link->hl_rxp != NULL) { m_freem(link->hl_rxp); link->hl_rxp = NULL; } /* SCO master ACL link */ if (link->hl_link != NULL) { hci_acl_close(link->hl_link, err); link->hl_link = NULL; } /* SCO pcb */ if (link->hl_sco != NULL) { struct sco_pcb *pcb; pcb = link->hl_sco; pcb->sp_link = NULL; link->hl_sco = NULL; (*pcb->sp_proto->disconnected)(pcb->sp_upper, err); } /* flush any SCO data */ MBUFQ_DRAIN(&link->hl_data); /* * Halt the callout - if its already running we cannot free the * link structure but the timeout function will call us back in * any case. */ link->hl_state = HCI_LINK_CLOSED; callout_stop(&link->hl_expire); if (callout_invoking(&link->hl_expire)) return; TAILQ_REMOVE(&link->hl_unit->hci_links, link, hl_next); free(link, M_BLUETOOTH); } /* * Lookup HCI link by address and type. Note that for SCO links there may * be more than one link per address, so we only return links with no * handle (ie new links) */ struct hci_link * hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint16_t type) { struct hci_link *link; KASSERT(unit); KASSERT(bdaddr); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (link->hl_type != type) continue; if (type == HCI_LINK_SCO && link->hl_handle != 0) continue; if (bdaddr_same(&link->hl_bdaddr, bdaddr)) break; } return link; } struct hci_link * hci_link_lookup_handle(struct hci_unit *unit, uint16_t handle) { struct hci_link *link; KASSERT(unit); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (handle == link->hl_handle) break; } return link; } @ 1.1.8.1 log @file hci_link.c was added on branch gdamore-uart on 2006-07-13 17:49:58 +0000 @ text @d1 790 @ 1.1.8.2 log @Merge from HEAD. @ text @a0 790 /* $NetBSD: hci_link.c,v 1.1.8.1 2006/07/13 17:49:58 gdamore Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. * Copyright (c) 2006 Itronix Inc. * 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. * 3. The name of Itronix Inc. may not be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. */ #include __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.8.1 2006/07/13 17:49:58 gdamore Exp $"); #include #include #include #include #include #include #include #include #include #include #include /******************************************************************************* * * HCI ACL Connections */ /* * Automatically expire unused ACL connections after this number of * seconds (if zero, do not expire unused connections) [sysctl] */ int hci_acl_expiry = 10; /* seconds */ /* * hci_acl_open(unit, bdaddr) * * open ACL connection to remote bdaddr. Only one ACL connection is permitted * between any two Bluetooth devices, so we look for an existing one before * trying to start a new one. */ struct hci_link * hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; hci_create_con_cp cp; int err; KASSERT(unit); KASSERT(bdaddr); link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link == NULL) { link = hci_link_alloc(unit); if (link == NULL) return NULL; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); } switch(link->hl_state) { case HCI_LINK_CLOSED: /* * open connection to remote device */ memset(&cp, 0, sizeof(cp)); bdaddr_copy(&cp.bdaddr, bdaddr); cp.pkt_type = htole16(unit->hci_packet_type); if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH) cp.accept_role_switch = 1; err = hci_send_cmd(unit, HCI_CMD_CREATE_CON, &cp, sizeof(cp)); if (err) { hci_link_free(link, err); return NULL; } link->hl_state = HCI_LINK_WAIT_CONNECT; break; case HCI_LINK_WAIT_CONNECT: /* * somebody else already trying to connect, we just * sit on the bench with them.. */ break; case HCI_LINK_OPEN: /* * If already open, halt any expiry timeouts. We dont need * to care about already invoking timeouts since refcnt >0 * will keep the link alive. */ callout_stop(&link->hl_expire); break; default: UNKNOWN(link->hl_state); return NULL; } /* open */ link->hl_refcnt++; return link; } /* * Close ACL connection. When there are no more references to this link, * we can either close it down or schedule a delayed closedown. */ void hci_acl_close(struct hci_link *link, int err) { KASSERT(link); if (--link->hl_refcnt == 0) { if (link->hl_state == HCI_LINK_CLOSED) hci_link_free(link, err); else if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } } /* * Incoming ACL connection. For now, we accept all connections but it * would be better to check the L2CAP listen list and only accept when * there is a listener available. */ struct hci_link * hci_acl_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; link = hci_link_alloc(unit); if (link != NULL) { link->hl_state = HCI_LINK_WAIT_CONNECT; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } return link; } void hci_acl_timeout(void *arg) { struct hci_link *link = arg; hci_discon_cp cp; int s, err; s = splsoftnet(); callout_ack(&link->hl_expire); if (link->hl_refcnt > 0) goto out; DPRINTF("link #%d expired\n", link->hl_handle); switch (link->hl_state) { case HCI_LINK_CLOSED: case HCI_LINK_WAIT_CONNECT: hci_link_free(link, ECONNRESET); break; case HCI_LINK_OPEN: cp.con_handle = htole16(link->hl_handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ err = hci_send_cmd(link->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); if (err) DPRINTF("error %d sending HCI_CMD_DISCONNECT\n", err); break; default: UNKNOWN(link->hl_state); break; } out: splx(s); } /* * Receive ACL Data * * we accumulate packet fragments on the hci_link structure * until a full L2CAP frame is ready, then send it on. */ void hci_acl_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_acldata_hdr_t hdr; uint16_t handle, want; int pb, got; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_ACL_DATA_PKT) { printf("%s: bad ACL packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != le16toh(hdr.length)) { printf("%s: bad ACL packet length\n", unit->hci_devname); goto bad; } #endif hdr.length = le16toh(hdr.length); hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); pb = HCI_PB_FLAG(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL) { hci_discon_cp cp; DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); /* * There is no way to find out what this connection handle is * for, just get rid of it. This may happen, if a USB dongle * is plugged into a self powered hub and does not reset when * the system is shut down. */ cp.con_handle = htole16(handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); goto bad; } switch (pb) { case HCI_PACKET_START: if (link->hl_rxp != NULL) printf("%s: dropped incomplete ACL packet\n", unit->hci_devname); if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) { printf("%s: short ACL packet\n", unit->hci_devname); goto bad; } link->hl_rxp = m; got = m->m_pkthdr.len; break; case HCI_PACKET_FRAGMENT: if (link->hl_rxp == NULL) { printf("%s: unexpected packet fragment\n", unit->hci_devname); goto bad; } got = m->m_pkthdr.len + link->hl_rxp->m_pkthdr.len; m_cat(link->hl_rxp, m); m = link->hl_rxp; m->m_pkthdr.len = got; break; default: printf("%s: unknown packet type\n", unit->hci_devname); goto bad; } m_copydata(m, 0, sizeof(want), &want); want = le16toh(want) + sizeof(l2cap_hdr_t) - got; if (want > 0) return; link->hl_rxp = NULL; if (want == 0) { l2cap_recv_frame(m, link); return; } bad: m_freem(m); } /* * Send ACL data on link * * We must fragment packets into chunks of less than unit->hci_max_acl_size and * prepend a relevant ACL header to each fragment. We keep a PDU structure * attached to the link, so that completed fragments can be marked off and * more data requested from above once the PDU is sent. */ int hci_acl_send(struct mbuf *m, struct hci_link *link, struct l2cap_channel *chan) { struct l2cap_pdu *pdu; struct mbuf *n = NULL; int plen, mlen, num = 0; KASSERT(link); KASSERT(m); KASSERT(m->m_flags & M_PKTHDR); KASSERT(m->m_pkthdr.len > 0); if (link->hl_state == HCI_LINK_CLOSED) { m_freem(m); return ENETDOWN; } pdu = pool_get(&l2cap_pdu_pool, PR_NOWAIT); if (pdu == NULL) goto nomem; pdu->lp_chan = chan; pdu->lp_pending = 0; MBUFQ_INIT(&pdu->lp_data); plen = m->m_pkthdr.len; mlen = link->hl_unit->hci_max_acl_size; DPRINTFN(5, "%s: handle #%d, plen = %d, max = %d\n", link->hl_unit->hci_devname, link->hl_handle, plen, mlen); while (plen > 0) { if (plen > mlen) { n = m_split(m, mlen, M_DONTWAIT); if (n == NULL) goto nomem; } else { mlen = plen; } if (num++ == 0) m->m_flags |= M_PROTO1; /* tag first fragment */ DPRINTFN(10, "chunk of %d (plen = %d) bytes\n", mlen, plen); MBUFQ_ENQUEUE(&pdu->lp_data, m); m = n; plen -= mlen; } TAILQ_INSERT_TAIL(&link->hl_txq, pdu, lp_next); link->hl_txqlen += num; hci_acl_start(link); return 0; nomem: if (m) m_freem(m); if (n) m_freem(n); if (pdu) { MBUFQ_DRAIN(&pdu->lp_data); pool_put(&l2cap_pdu_pool, pdu); } return ENOMEM; } /* * Start sending ACL data on link. * * We may use all the available packet slots. The reason that we add * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP * signal packets may be queued before the handle is given to us.. * * this is called from hci_acl_send() above, and the event processing * code (for CON_COMPL and NUM_COMPL_PKTS) */ void hci_acl_start(struct hci_link *link) { struct hci_unit *unit; hci_acldata_hdr_t *hdr; struct l2cap_pdu *pdu; struct mbuf *m; uint16_t handle; KASSERT(link); unit = link->hl_unit; KASSERT(unit); /* this is mainly to block ourselves (below) */ if (link->hl_state != HCI_LINK_OPEN) return; if (link->hl_txqlen == 0 || unit->hci_num_acl_pkts == 0) return; /* find first PDU with data to send */ pdu = TAILQ_FIRST(&link->hl_txq); for (;;) { if (pdu == NULL) return; if (MBUFQ_FIRST(&pdu->lp_data) != NULL) break; pdu = TAILQ_NEXT(pdu, lp_next); } while (unit->hci_num_acl_pkts > 0) { MBUFQ_DEQUEUE(&pdu->lp_data, m); KASSERT(m != NULL); if (m->m_flags & M_PROTO1) handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_START, 0); else handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_FRAGMENT, 0); M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) break; hdr = mtod(m, hci_acldata_hdr_t *); hdr->type = HCI_ACL_DATA_PKT; hdr->con_handle = htole16(handle); hdr->length = htole16(m->m_pkthdr.len - sizeof(*hdr)); link->hl_txqlen--; pdu->lp_pending++; hci_output_acl(unit, m); if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { if (pdu->lp_chan) { /* * This should enable streaming of PDUs - when * we have placed all the fragments on the acl * output queue, we trigger the L2CAP layer to * send us down one more. Use a false state so * we dont run into ourselves coming back from * the future.. */ link->hl_state = HCI_LINK_BLOCK; l2cap_start(pdu->lp_chan); link->hl_state = HCI_LINK_OPEN; } pdu = TAILQ_NEXT(pdu, lp_next); if (pdu == NULL) break; } } /* * We had our turn now, move to the back of the queue to let * other links have a go at the output buffers.. */ if (TAILQ_NEXT(link, hl_next)) { TAILQ_REMOVE(&unit->hci_links, link, hl_next); TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); } } /* * Confirm ACL packets cleared from Controller buffers. We scan our PDU * list to clear pending fragments and signal upstream for more data * when a PDU is complete. */ void hci_acl_complete(struct hci_link *link, int num) { struct l2cap_pdu *pdu; struct l2cap_channel *chan; DPRINTFN(5, "handle #%d (%d)\n", link->hl_handle, num); while (num > 0) { pdu = TAILQ_FIRST(&link->hl_txq); if (pdu == NULL) { printf("%s: %d packets completed on handle #%x " "but none pending!\n", link->hl_unit->hci_devname, num, link->hl_handle); return; } if (num >= pdu->lp_pending) { num -= pdu->lp_pending; pdu->lp_pending = 0; if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); chan = pdu->lp_chan; if (chan != NULL) { chan->lc_pending--; (*chan->lc_proto->complete) (chan->lc_upper, 1); if (chan->lc_pending == 0) l2cap_start(chan); } pool_put(&l2cap_pdu_pool, pdu); } } else { pdu->lp_pending -= num; num = 0; } } } /******************************************************************************* * * HCI SCO Connections */ /* * Incoming SCO Connection. Not yet implemented */ struct hci_link * hci_sco_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { return NULL; } /* * receive SCO packet, we only need to strip the header and send * it to the right handler */ void hci_sco_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_scodata_hdr_t hdr; uint16_t handle; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_SCO_DATA_PKT) { printf("%s: bad SCO packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != hdr.length) { printf("%s: bad SCO packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, hdr.length); goto bad; } #endif hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL || link->hl_type == HCI_LINK_ACL) { DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); goto bad; } (*link->hl_sco->sp_proto->input)(link->hl_sco->sp_upper, m); return; bad: m_freem(m); } void hci_sco_start(struct hci_link *link) { } /* * SCO packets have completed at the controller, so we can * signal up to free the buffer space. */ void hci_sco_complete(struct hci_link *link, int num) { DPRINTFN(5, "handle #%d (num=%d)\n", link->hl_handle, num); link->hl_sco->sp_pending--; (*link->hl_sco->sp_proto->complete)(link->hl_sco->sp_upper, num); } /******************************************************************************* * * Generic HCI Connection alloc/free/lookup etc */ struct hci_link * hci_link_alloc(struct hci_unit *unit) { struct hci_link *link; KASSERT(unit); link = malloc(sizeof(struct hci_link), M_BLUETOOTH, M_NOWAIT | M_ZERO); if (link == NULL) return NULL; link->hl_unit = unit; link->hl_state = HCI_LINK_CLOSED; /* init ACL portion */ callout_init(&link->hl_expire); callout_setfunc(&link->hl_expire, hci_acl_timeout, link); TAILQ_INIT(&link->hl_txq); /* outgoing packets */ TAILQ_INIT(&link->hl_reqs); /* request queue */ link->hl_mtu = L2CAP_MTU_DEFAULT; /* L2CAP signal mtu */ link->hl_flush = L2CAP_FLUSH_TIMO_DEFAULT; /* flush timeout */ /* init SCO portion */ MBUFQ_INIT(&link->hl_data); /* attach to unit */ TAILQ_INSERT_HEAD(&unit->hci_links, link, hl_next); return link; } void hci_link_free(struct hci_link *link, int err) { struct l2cap_req *req; struct l2cap_pdu *pdu; struct l2cap_channel *chan; KASSERT(link); DPRINTF("#%d, type = %d, state = %d, refcnt = %d\n", link->hl_handle, link->hl_type, link->hl_state, link->hl_refcnt); /* ACL reference count */ if (link->hl_refcnt > 0) { LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { if (chan->lc_link == link) l2cap_close(chan, err); } } KASSERT(link->hl_refcnt == 0); /* ACL L2CAP requests.. */ while ((req = TAILQ_FIRST(&link->hl_reqs)) != NULL) l2cap_request_free(req); KASSERT(TAILQ_EMPTY(&link->hl_reqs)); /* ACL outgoing data queue */ while ((pdu = TAILQ_FIRST(&link->hl_txq)) != NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); MBUFQ_DRAIN(&pdu->lp_data); if (pdu->lp_pending) link->hl_unit->hci_num_acl_pkts += pdu->lp_pending; pool_put(&l2cap_pdu_pool, pdu); } KASSERT(TAILQ_EMPTY(&link->hl_txq)); /* ACL incoming data packet */ if (link->hl_rxp != NULL) { m_freem(link->hl_rxp); link->hl_rxp = NULL; } /* SCO master ACL link */ if (link->hl_link != NULL) { hci_acl_close(link->hl_link, err); link->hl_link = NULL; } /* SCO pcb */ if (link->hl_sco != NULL) { struct sco_pcb *pcb; pcb = link->hl_sco; pcb->sp_link = NULL; link->hl_sco = NULL; (*pcb->sp_proto->disconnected)(pcb->sp_upper, err); } /* flush any SCO data */ MBUFQ_DRAIN(&link->hl_data); /* * Halt the callout - if its already running we cannot free the * link structure but the timeout function will call us back in * any case. */ link->hl_state = HCI_LINK_CLOSED; callout_stop(&link->hl_expire); if (callout_invoking(&link->hl_expire)) return; TAILQ_REMOVE(&link->hl_unit->hci_links, link, hl_next); free(link, M_BLUETOOTH); } /* * Lookup HCI link by address and type. Note that for SCO links there may * be more than one link per address, so we only return links with no * handle (ie new links) */ struct hci_link * hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint16_t type) { struct hci_link *link; KASSERT(unit); KASSERT(bdaddr); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (link->hl_type != type) continue; if (type == HCI_LINK_SCO && link->hl_handle != 0) continue; if (bdaddr_same(&link->hl_bdaddr, bdaddr)) break; } return link; } struct hci_link * hci_link_lookup_handle(struct hci_unit *unit, uint16_t handle) { struct hci_link *link; KASSERT(unit); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (handle == link->hl_handle) break; } return link; } @ 1.1.6.1 log @file hci_link.c was added on branch yamt-pdpolicy on 2006-06-26 12:53:57 +0000 @ text @d1 790 @ 1.1.6.2 log @sync with head. @ text @a0 790 /* $NetBSD: hci_link.c,v 1.1.6.1 2006/06/26 12:53:57 yamt Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. * Copyright (c) 2006 Itronix Inc. * 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. * 3. The name of Itronix Inc. may not be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. */ #include __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.6.1 2006/06/26 12:53:57 yamt Exp $"); #include #include #include #include #include #include #include #include #include #include #include /******************************************************************************* * * HCI ACL Connections */ /* * Automatically expire unused ACL connections after this number of * seconds (if zero, do not expire unused connections) [sysctl] */ int hci_acl_expiry = 10; /* seconds */ /* * hci_acl_open(unit, bdaddr) * * open ACL connection to remote bdaddr. Only one ACL connection is permitted * between any two Bluetooth devices, so we look for an existing one before * trying to start a new one. */ struct hci_link * hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; hci_create_con_cp cp; int err; KASSERT(unit); KASSERT(bdaddr); link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link == NULL) { link = hci_link_alloc(unit); if (link == NULL) return NULL; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); } switch(link->hl_state) { case HCI_LINK_CLOSED: /* * open connection to remote device */ memset(&cp, 0, sizeof(cp)); bdaddr_copy(&cp.bdaddr, bdaddr); cp.pkt_type = htole16(unit->hci_packet_type); if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH) cp.accept_role_switch = 1; err = hci_send_cmd(unit, HCI_CMD_CREATE_CON, &cp, sizeof(cp)); if (err) { hci_link_free(link, err); return NULL; } link->hl_state = HCI_LINK_WAIT_CONNECT; break; case HCI_LINK_WAIT_CONNECT: /* * somebody else already trying to connect, we just * sit on the bench with them.. */ break; case HCI_LINK_OPEN: /* * If already open, halt any expiry timeouts. We dont need * to care about already invoking timeouts since refcnt >0 * will keep the link alive. */ callout_stop(&link->hl_expire); break; default: UNKNOWN(link->hl_state); return NULL; } /* open */ link->hl_refcnt++; return link; } /* * Close ACL connection. When there are no more references to this link, * we can either close it down or schedule a delayed closedown. */ void hci_acl_close(struct hci_link *link, int err) { KASSERT(link); if (--link->hl_refcnt == 0) { if (link->hl_state == HCI_LINK_CLOSED) hci_link_free(link, err); else if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } } /* * Incoming ACL connection. For now, we accept all connections but it * would be better to check the L2CAP listen list and only accept when * there is a listener available. */ struct hci_link * hci_acl_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; link = hci_link_alloc(unit); if (link != NULL) { link->hl_state = HCI_LINK_WAIT_CONNECT; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } return link; } void hci_acl_timeout(void *arg) { struct hci_link *link = arg; hci_discon_cp cp; int s, err; s = splsoftnet(); callout_ack(&link->hl_expire); if (link->hl_refcnt > 0) goto out; DPRINTF("link #%d expired\n", link->hl_handle); switch (link->hl_state) { case HCI_LINK_CLOSED: case HCI_LINK_WAIT_CONNECT: hci_link_free(link, ECONNRESET); break; case HCI_LINK_OPEN: cp.con_handle = htole16(link->hl_handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ err = hci_send_cmd(link->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); if (err) DPRINTF("error %d sending HCI_CMD_DISCONNECT\n", err); break; default: UNKNOWN(link->hl_state); break; } out: splx(s); } /* * Receive ACL Data * * we accumulate packet fragments on the hci_link structure * until a full L2CAP frame is ready, then send it on. */ void hci_acl_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_acldata_hdr_t hdr; uint16_t handle, want; int pb, got; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_ACL_DATA_PKT) { printf("%s: bad ACL packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != le16toh(hdr.length)) { printf("%s: bad ACL packet length\n", unit->hci_devname); goto bad; } #endif hdr.length = le16toh(hdr.length); hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); pb = HCI_PB_FLAG(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL) { hci_discon_cp cp; DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); /* * There is no way to find out what this connection handle is * for, just get rid of it. This may happen, if a USB dongle * is plugged into a self powered hub and does not reset when * the system is shut down. */ cp.con_handle = htole16(handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); goto bad; } switch (pb) { case HCI_PACKET_START: if (link->hl_rxp != NULL) printf("%s: dropped incomplete ACL packet\n", unit->hci_devname); if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) { printf("%s: short ACL packet\n", unit->hci_devname); goto bad; } link->hl_rxp = m; got = m->m_pkthdr.len; break; case HCI_PACKET_FRAGMENT: if (link->hl_rxp == NULL) { printf("%s: unexpected packet fragment\n", unit->hci_devname); goto bad; } got = m->m_pkthdr.len + link->hl_rxp->m_pkthdr.len; m_cat(link->hl_rxp, m); m = link->hl_rxp; m->m_pkthdr.len = got; break; default: printf("%s: unknown packet type\n", unit->hci_devname); goto bad; } m_copydata(m, 0, sizeof(want), &want); want = le16toh(want) + sizeof(l2cap_hdr_t) - got; if (want > 0) return; link->hl_rxp = NULL; if (want == 0) { l2cap_recv_frame(m, link); return; } bad: m_freem(m); } /* * Send ACL data on link * * We must fragment packets into chunks of less than unit->hci_max_acl_size and * prepend a relevant ACL header to each fragment. We keep a PDU structure * attached to the link, so that completed fragments can be marked off and * more data requested from above once the PDU is sent. */ int hci_acl_send(struct mbuf *m, struct hci_link *link, struct l2cap_channel *chan) { struct l2cap_pdu *pdu; struct mbuf *n = NULL; int plen, mlen, num = 0; KASSERT(link); KASSERT(m); KASSERT(m->m_flags & M_PKTHDR); KASSERT(m->m_pkthdr.len > 0); if (link->hl_state == HCI_LINK_CLOSED) { m_freem(m); return ENETDOWN; } pdu = pool_get(&l2cap_pdu_pool, PR_NOWAIT); if (pdu == NULL) goto nomem; pdu->lp_chan = chan; pdu->lp_pending = 0; MBUFQ_INIT(&pdu->lp_data); plen = m->m_pkthdr.len; mlen = link->hl_unit->hci_max_acl_size; DPRINTFN(5, "%s: handle #%d, plen = %d, max = %d\n", link->hl_unit->hci_devname, link->hl_handle, plen, mlen); while (plen > 0) { if (plen > mlen) { n = m_split(m, mlen, M_DONTWAIT); if (n == NULL) goto nomem; } else { mlen = plen; } if (num++ == 0) m->m_flags |= M_PROTO1; /* tag first fragment */ DPRINTFN(10, "chunk of %d (plen = %d) bytes\n", mlen, plen); MBUFQ_ENQUEUE(&pdu->lp_data, m); m = n; plen -= mlen; } TAILQ_INSERT_TAIL(&link->hl_txq, pdu, lp_next); link->hl_txqlen += num; hci_acl_start(link); return 0; nomem: if (m) m_freem(m); if (n) m_freem(n); if (pdu) { MBUFQ_DRAIN(&pdu->lp_data); pool_put(&l2cap_pdu_pool, pdu); } return ENOMEM; } /* * Start sending ACL data on link. * * We may use all the available packet slots. The reason that we add * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP * signal packets may be queued before the handle is given to us.. * * this is called from hci_acl_send() above, and the event processing * code (for CON_COMPL and NUM_COMPL_PKTS) */ void hci_acl_start(struct hci_link *link) { struct hci_unit *unit; hci_acldata_hdr_t *hdr; struct l2cap_pdu *pdu; struct mbuf *m; uint16_t handle; KASSERT(link); unit = link->hl_unit; KASSERT(unit); /* this is mainly to block ourselves (below) */ if (link->hl_state != HCI_LINK_OPEN) return; if (link->hl_txqlen == 0 || unit->hci_num_acl_pkts == 0) return; /* find first PDU with data to send */ pdu = TAILQ_FIRST(&link->hl_txq); for (;;) { if (pdu == NULL) return; if (MBUFQ_FIRST(&pdu->lp_data) != NULL) break; pdu = TAILQ_NEXT(pdu, lp_next); } while (unit->hci_num_acl_pkts > 0) { MBUFQ_DEQUEUE(&pdu->lp_data, m); KASSERT(m != NULL); if (m->m_flags & M_PROTO1) handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_START, 0); else handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_FRAGMENT, 0); M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) break; hdr = mtod(m, hci_acldata_hdr_t *); hdr->type = HCI_ACL_DATA_PKT; hdr->con_handle = htole16(handle); hdr->length = htole16(m->m_pkthdr.len - sizeof(*hdr)); link->hl_txqlen--; pdu->lp_pending++; hci_output_acl(unit, m); if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { if (pdu->lp_chan) { /* * This should enable streaming of PDUs - when * we have placed all the fragments on the acl * output queue, we trigger the L2CAP layer to * send us down one more. Use a false state so * we dont run into ourselves coming back from * the future.. */ link->hl_state = HCI_LINK_BLOCK; l2cap_start(pdu->lp_chan); link->hl_state = HCI_LINK_OPEN; } pdu = TAILQ_NEXT(pdu, lp_next); if (pdu == NULL) break; } } /* * We had our turn now, move to the back of the queue to let * other links have a go at the output buffers.. */ if (TAILQ_NEXT(link, hl_next)) { TAILQ_REMOVE(&unit->hci_links, link, hl_next); TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); } } /* * Confirm ACL packets cleared from Controller buffers. We scan our PDU * list to clear pending fragments and signal upstream for more data * when a PDU is complete. */ void hci_acl_complete(struct hci_link *link, int num) { struct l2cap_pdu *pdu; struct l2cap_channel *chan; DPRINTFN(5, "handle #%d (%d)\n", link->hl_handle, num); while (num > 0) { pdu = TAILQ_FIRST(&link->hl_txq); if (pdu == NULL) { printf("%s: %d packets completed on handle #%x " "but none pending!\n", link->hl_unit->hci_devname, num, link->hl_handle); return; } if (num >= pdu->lp_pending) { num -= pdu->lp_pending; pdu->lp_pending = 0; if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); chan = pdu->lp_chan; if (chan != NULL) { chan->lc_pending--; (*chan->lc_proto->complete) (chan->lc_upper, 1); if (chan->lc_pending == 0) l2cap_start(chan); } pool_put(&l2cap_pdu_pool, pdu); } } else { pdu->lp_pending -= num; num = 0; } } } /******************************************************************************* * * HCI SCO Connections */ /* * Incoming SCO Connection. Not yet implemented */ struct hci_link * hci_sco_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { return NULL; } /* * receive SCO packet, we only need to strip the header and send * it to the right handler */ void hci_sco_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_scodata_hdr_t hdr; uint16_t handle; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_SCO_DATA_PKT) { printf("%s: bad SCO packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != hdr.length) { printf("%s: bad SCO packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, hdr.length); goto bad; } #endif hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL || link->hl_type == HCI_LINK_ACL) { DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); goto bad; } (*link->hl_sco->sp_proto->input)(link->hl_sco->sp_upper, m); return; bad: m_freem(m); } void hci_sco_start(struct hci_link *link) { } /* * SCO packets have completed at the controller, so we can * signal up to free the buffer space. */ void hci_sco_complete(struct hci_link *link, int num) { DPRINTFN(5, "handle #%d (num=%d)\n", link->hl_handle, num); link->hl_sco->sp_pending--; (*link->hl_sco->sp_proto->complete)(link->hl_sco->sp_upper, num); } /******************************************************************************* * * Generic HCI Connection alloc/free/lookup etc */ struct hci_link * hci_link_alloc(struct hci_unit *unit) { struct hci_link *link; KASSERT(unit); link = malloc(sizeof(struct hci_link), M_BLUETOOTH, M_NOWAIT | M_ZERO); if (link == NULL) return NULL; link->hl_unit = unit; link->hl_state = HCI_LINK_CLOSED; /* init ACL portion */ callout_init(&link->hl_expire); callout_setfunc(&link->hl_expire, hci_acl_timeout, link); TAILQ_INIT(&link->hl_txq); /* outgoing packets */ TAILQ_INIT(&link->hl_reqs); /* request queue */ link->hl_mtu = L2CAP_MTU_DEFAULT; /* L2CAP signal mtu */ link->hl_flush = L2CAP_FLUSH_TIMO_DEFAULT; /* flush timeout */ /* init SCO portion */ MBUFQ_INIT(&link->hl_data); /* attach to unit */ TAILQ_INSERT_HEAD(&unit->hci_links, link, hl_next); return link; } void hci_link_free(struct hci_link *link, int err) { struct l2cap_req *req; struct l2cap_pdu *pdu; struct l2cap_channel *chan; KASSERT(link); DPRINTF("#%d, type = %d, state = %d, refcnt = %d\n", link->hl_handle, link->hl_type, link->hl_state, link->hl_refcnt); /* ACL reference count */ if (link->hl_refcnt > 0) { LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { if (chan->lc_link == link) l2cap_close(chan, err); } } KASSERT(link->hl_refcnt == 0); /* ACL L2CAP requests.. */ while ((req = TAILQ_FIRST(&link->hl_reqs)) != NULL) l2cap_request_free(req); KASSERT(TAILQ_EMPTY(&link->hl_reqs)); /* ACL outgoing data queue */ while ((pdu = TAILQ_FIRST(&link->hl_txq)) != NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); MBUFQ_DRAIN(&pdu->lp_data); if (pdu->lp_pending) link->hl_unit->hci_num_acl_pkts += pdu->lp_pending; pool_put(&l2cap_pdu_pool, pdu); } KASSERT(TAILQ_EMPTY(&link->hl_txq)); /* ACL incoming data packet */ if (link->hl_rxp != NULL) { m_freem(link->hl_rxp); link->hl_rxp = NULL; } /* SCO master ACL link */ if (link->hl_link != NULL) { hci_acl_close(link->hl_link, err); link->hl_link = NULL; } /* SCO pcb */ if (link->hl_sco != NULL) { struct sco_pcb *pcb; pcb = link->hl_sco; pcb->sp_link = NULL; link->hl_sco = NULL; (*pcb->sp_proto->disconnected)(pcb->sp_upper, err); } /* flush any SCO data */ MBUFQ_DRAIN(&link->hl_data); /* * Halt the callout - if its already running we cannot free the * link structure but the timeout function will call us back in * any case. */ link->hl_state = HCI_LINK_CLOSED; callout_stop(&link->hl_expire); if (callout_invoking(&link->hl_expire)) return; TAILQ_REMOVE(&link->hl_unit->hci_links, link, hl_next); free(link, M_BLUETOOTH); } /* * Lookup HCI link by address and type. Note that for SCO links there may * be more than one link per address, so we only return links with no * handle (ie new links) */ struct hci_link * hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint16_t type) { struct hci_link *link; KASSERT(unit); KASSERT(bdaddr); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (link->hl_type != type) continue; if (type == HCI_LINK_SCO && link->hl_handle != 0) continue; if (bdaddr_same(&link->hl_bdaddr, bdaddr)) break; } return link; } struct hci_link * hci_link_lookup_handle(struct hci_unit *unit, uint16_t handle) { struct hci_link *link; KASSERT(unit); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (handle == link->hl_handle) break; } return link; } @ 1.1.6.3 log @sync with head @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1.6.2 2006/08/11 15:46:32 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.6.2 2006/08/11 15:46:32 yamt Exp $"); d46 1 a47 1 #include a70 1 struct hci_memo *memo; a94 8 memo = hci_memo_find(unit, bdaddr); if (memo != NULL) { cp.page_scan_rep_mode = memo->response.page_scan_rep_mode; cp.page_scan_mode = memo->response.page_scan_mode; cp.clock_offset = memo->response.clock_offset; } d153 3 a155 8 * Incoming ACL connection. * * For now, we accept all connections but it would be better to check * the L2CAP listen list and only accept when there is a listener * available. * * There should not be a link to the same bdaddr already, we check * anyway though its left unhandled for now. a161 4 link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link != NULL) return NULL; d559 1 a559 2 * Incoming SCO Connection. We check the list for anybody willing * to take it. a563 56 struct sockaddr_bt laddr, raddr; struct sco_pcb *pcb, *new; struct hci_link *sco, *acl; memset(&laddr, 0, sizeof(laddr)); laddr.bt_len = sizeof(laddr); laddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&laddr.bt_bdaddr, &unit->hci_bdaddr); memset(&raddr, 0, sizeof(raddr)); raddr.bt_len = sizeof(raddr); raddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&raddr.bt_bdaddr, bdaddr); /* * There should already be an ACL link up and running before * the controller sends us SCO connection requests, but you * never know.. */ acl = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (acl == NULL || acl->hl_state != HCI_LINK_OPEN) return NULL; LIST_FOREACH(pcb, &sco_pcb, sp_next) { if ((pcb->sp_flags & SP_LISTENING) == 0) continue; new = (*pcb->sp_proto->newconn)(pcb->sp_upper, &laddr, &raddr); if (new == NULL) continue; /* * Ok, got new pcb so we can start a new link and fill * in all the details. */ bdaddr_copy(&new->sp_laddr, &unit->hci_bdaddr); bdaddr_copy(&new->sp_raddr, bdaddr); sco = hci_link_alloc(unit); if (sco == NULL) { sco_detach(&new); return NULL; } sco->hl_type = HCI_LINK_SCO; bdaddr_copy(&sco->hl_bdaddr, bdaddr); sco->hl_link = hci_acl_open(unit, bdaddr); KASSERT(sco->hl_link == acl); sco->hl_sco = new; new->sp_link = sco; new->sp_mtu = unit->hci_max_sco_size; return sco; } @ 1.1.6.4 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1.6.3 2006/09/14 12:31:55 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.6.3 2006/09/14 12:31:55 yamt Exp $"); d101 1 a101 1 cp.clock_offset = htole16(memo->response.clock_offset); d264 1 a264 2 printf("%s: bad ACL packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, le16toh(hdr.length)); d751 1 a751 1 struct l2cap_channel *chan, *next; d761 1 a761 3 next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); @ 1.1.2.1 log @file hci_link.c was added on branch yamt-lazymbuf on 2006-06-21 15:10:51 +0000 @ text @d1 790 @ 1.1.2.2 log @sync with head. @ text @a0 790 /* $NetBSD: hci_link.c,v 1.1.2.1 2006/06/21 15:10:51 yamt Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. * Copyright (c) 2006 Itronix Inc. * 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. * 3. The name of Itronix Inc. may not be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. */ #include __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.2.1 2006/06/21 15:10:51 yamt Exp $"); #include #include #include #include #include #include #include #include #include #include #include /******************************************************************************* * * HCI ACL Connections */ /* * Automatically expire unused ACL connections after this number of * seconds (if zero, do not expire unused connections) [sysctl] */ int hci_acl_expiry = 10; /* seconds */ /* * hci_acl_open(unit, bdaddr) * * open ACL connection to remote bdaddr. Only one ACL connection is permitted * between any two Bluetooth devices, so we look for an existing one before * trying to start a new one. */ struct hci_link * hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; hci_create_con_cp cp; int err; KASSERT(unit); KASSERT(bdaddr); link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link == NULL) { link = hci_link_alloc(unit); if (link == NULL) return NULL; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); } switch(link->hl_state) { case HCI_LINK_CLOSED: /* * open connection to remote device */ memset(&cp, 0, sizeof(cp)); bdaddr_copy(&cp.bdaddr, bdaddr); cp.pkt_type = htole16(unit->hci_packet_type); if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH) cp.accept_role_switch = 1; err = hci_send_cmd(unit, HCI_CMD_CREATE_CON, &cp, sizeof(cp)); if (err) { hci_link_free(link, err); return NULL; } link->hl_state = HCI_LINK_WAIT_CONNECT; break; case HCI_LINK_WAIT_CONNECT: /* * somebody else already trying to connect, we just * sit on the bench with them.. */ break; case HCI_LINK_OPEN: /* * If already open, halt any expiry timeouts. We dont need * to care about already invoking timeouts since refcnt >0 * will keep the link alive. */ callout_stop(&link->hl_expire); break; default: UNKNOWN(link->hl_state); return NULL; } /* open */ link->hl_refcnt++; return link; } /* * Close ACL connection. When there are no more references to this link, * we can either close it down or schedule a delayed closedown. */ void hci_acl_close(struct hci_link *link, int err) { KASSERT(link); if (--link->hl_refcnt == 0) { if (link->hl_state == HCI_LINK_CLOSED) hci_link_free(link, err); else if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } } /* * Incoming ACL connection. For now, we accept all connections but it * would be better to check the L2CAP listen list and only accept when * there is a listener available. */ struct hci_link * hci_acl_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { struct hci_link *link; link = hci_link_alloc(unit); if (link != NULL) { link->hl_state = HCI_LINK_WAIT_CONNECT; link->hl_type = HCI_LINK_ACL; bdaddr_copy(&link->hl_bdaddr, bdaddr); if (hci_acl_expiry > 0) callout_schedule(&link->hl_expire, hci_acl_expiry * hz); } return link; } void hci_acl_timeout(void *arg) { struct hci_link *link = arg; hci_discon_cp cp; int s, err; s = splsoftnet(); callout_ack(&link->hl_expire); if (link->hl_refcnt > 0) goto out; DPRINTF("link #%d expired\n", link->hl_handle); switch (link->hl_state) { case HCI_LINK_CLOSED: case HCI_LINK_WAIT_CONNECT: hci_link_free(link, ECONNRESET); break; case HCI_LINK_OPEN: cp.con_handle = htole16(link->hl_handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ err = hci_send_cmd(link->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); if (err) DPRINTF("error %d sending HCI_CMD_DISCONNECT\n", err); break; default: UNKNOWN(link->hl_state); break; } out: splx(s); } /* * Receive ACL Data * * we accumulate packet fragments on the hci_link structure * until a full L2CAP frame is ready, then send it on. */ void hci_acl_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_acldata_hdr_t hdr; uint16_t handle, want; int pb, got; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_ACL_DATA_PKT) { printf("%s: bad ACL packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != le16toh(hdr.length)) { printf("%s: bad ACL packet length\n", unit->hci_devname); goto bad; } #endif hdr.length = le16toh(hdr.length); hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); pb = HCI_PB_FLAG(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL) { hci_discon_cp cp; DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); /* * There is no way to find out what this connection handle is * for, just get rid of it. This may happen, if a USB dongle * is plugged into a self powered hub and does not reset when * the system is shut down. */ cp.con_handle = htole16(handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); goto bad; } switch (pb) { case HCI_PACKET_START: if (link->hl_rxp != NULL) printf("%s: dropped incomplete ACL packet\n", unit->hci_devname); if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) { printf("%s: short ACL packet\n", unit->hci_devname); goto bad; } link->hl_rxp = m; got = m->m_pkthdr.len; break; case HCI_PACKET_FRAGMENT: if (link->hl_rxp == NULL) { printf("%s: unexpected packet fragment\n", unit->hci_devname); goto bad; } got = m->m_pkthdr.len + link->hl_rxp->m_pkthdr.len; m_cat(link->hl_rxp, m); m = link->hl_rxp; m->m_pkthdr.len = got; break; default: printf("%s: unknown packet type\n", unit->hci_devname); goto bad; } m_copydata(m, 0, sizeof(want), &want); want = le16toh(want) + sizeof(l2cap_hdr_t) - got; if (want > 0) return; link->hl_rxp = NULL; if (want == 0) { l2cap_recv_frame(m, link); return; } bad: m_freem(m); } /* * Send ACL data on link * * We must fragment packets into chunks of less than unit->hci_max_acl_size and * prepend a relevant ACL header to each fragment. We keep a PDU structure * attached to the link, so that completed fragments can be marked off and * more data requested from above once the PDU is sent. */ int hci_acl_send(struct mbuf *m, struct hci_link *link, struct l2cap_channel *chan) { struct l2cap_pdu *pdu; struct mbuf *n = NULL; int plen, mlen, num = 0; KASSERT(link); KASSERT(m); KASSERT(m->m_flags & M_PKTHDR); KASSERT(m->m_pkthdr.len > 0); if (link->hl_state == HCI_LINK_CLOSED) { m_freem(m); return ENETDOWN; } pdu = pool_get(&l2cap_pdu_pool, PR_NOWAIT); if (pdu == NULL) goto nomem; pdu->lp_chan = chan; pdu->lp_pending = 0; MBUFQ_INIT(&pdu->lp_data); plen = m->m_pkthdr.len; mlen = link->hl_unit->hci_max_acl_size; DPRINTFN(5, "%s: handle #%d, plen = %d, max = %d\n", link->hl_unit->hci_devname, link->hl_handle, plen, mlen); while (plen > 0) { if (plen > mlen) { n = m_split(m, mlen, M_DONTWAIT); if (n == NULL) goto nomem; } else { mlen = plen; } if (num++ == 0) m->m_flags |= M_PROTO1; /* tag first fragment */ DPRINTFN(10, "chunk of %d (plen = %d) bytes\n", mlen, plen); MBUFQ_ENQUEUE(&pdu->lp_data, m); m = n; plen -= mlen; } TAILQ_INSERT_TAIL(&link->hl_txq, pdu, lp_next); link->hl_txqlen += num; hci_acl_start(link); return 0; nomem: if (m) m_freem(m); if (n) m_freem(n); if (pdu) { MBUFQ_DRAIN(&pdu->lp_data); pool_put(&l2cap_pdu_pool, pdu); } return ENOMEM; } /* * Start sending ACL data on link. * * We may use all the available packet slots. The reason that we add * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP * signal packets may be queued before the handle is given to us.. * * this is called from hci_acl_send() above, and the event processing * code (for CON_COMPL and NUM_COMPL_PKTS) */ void hci_acl_start(struct hci_link *link) { struct hci_unit *unit; hci_acldata_hdr_t *hdr; struct l2cap_pdu *pdu; struct mbuf *m; uint16_t handle; KASSERT(link); unit = link->hl_unit; KASSERT(unit); /* this is mainly to block ourselves (below) */ if (link->hl_state != HCI_LINK_OPEN) return; if (link->hl_txqlen == 0 || unit->hci_num_acl_pkts == 0) return; /* find first PDU with data to send */ pdu = TAILQ_FIRST(&link->hl_txq); for (;;) { if (pdu == NULL) return; if (MBUFQ_FIRST(&pdu->lp_data) != NULL) break; pdu = TAILQ_NEXT(pdu, lp_next); } while (unit->hci_num_acl_pkts > 0) { MBUFQ_DEQUEUE(&pdu->lp_data, m); KASSERT(m != NULL); if (m->m_flags & M_PROTO1) handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_START, 0); else handle = HCI_MK_CON_HANDLE(link->hl_handle, HCI_PACKET_FRAGMENT, 0); M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) break; hdr = mtod(m, hci_acldata_hdr_t *); hdr->type = HCI_ACL_DATA_PKT; hdr->con_handle = htole16(handle); hdr->length = htole16(m->m_pkthdr.len - sizeof(*hdr)); link->hl_txqlen--; pdu->lp_pending++; hci_output_acl(unit, m); if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { if (pdu->lp_chan) { /* * This should enable streaming of PDUs - when * we have placed all the fragments on the acl * output queue, we trigger the L2CAP layer to * send us down one more. Use a false state so * we dont run into ourselves coming back from * the future.. */ link->hl_state = HCI_LINK_BLOCK; l2cap_start(pdu->lp_chan); link->hl_state = HCI_LINK_OPEN; } pdu = TAILQ_NEXT(pdu, lp_next); if (pdu == NULL) break; } } /* * We had our turn now, move to the back of the queue to let * other links have a go at the output buffers.. */ if (TAILQ_NEXT(link, hl_next)) { TAILQ_REMOVE(&unit->hci_links, link, hl_next); TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); } } /* * Confirm ACL packets cleared from Controller buffers. We scan our PDU * list to clear pending fragments and signal upstream for more data * when a PDU is complete. */ void hci_acl_complete(struct hci_link *link, int num) { struct l2cap_pdu *pdu; struct l2cap_channel *chan; DPRINTFN(5, "handle #%d (%d)\n", link->hl_handle, num); while (num > 0) { pdu = TAILQ_FIRST(&link->hl_txq); if (pdu == NULL) { printf("%s: %d packets completed on handle #%x " "but none pending!\n", link->hl_unit->hci_devname, num, link->hl_handle); return; } if (num >= pdu->lp_pending) { num -= pdu->lp_pending; pdu->lp_pending = 0; if (MBUFQ_FIRST(&pdu->lp_data) == NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); chan = pdu->lp_chan; if (chan != NULL) { chan->lc_pending--; (*chan->lc_proto->complete) (chan->lc_upper, 1); if (chan->lc_pending == 0) l2cap_start(chan); } pool_put(&l2cap_pdu_pool, pdu); } } else { pdu->lp_pending -= num; num = 0; } } } /******************************************************************************* * * HCI SCO Connections */ /* * Incoming SCO Connection. Not yet implemented */ struct hci_link * hci_sco_newconn(struct hci_unit *unit, bdaddr_t *bdaddr) { return NULL; } /* * receive SCO packet, we only need to strip the header and send * it to the right handler */ void hci_sco_recv(struct mbuf *m, struct hci_unit *unit) { struct hci_link *link; hci_scodata_hdr_t hdr; uint16_t handle; KASSERT(m); KASSERT(unit); KASSERT(m->m_pkthdr.len >= sizeof(hdr)); m_copydata(m, 0, sizeof(hdr), &hdr); m_adj(m, sizeof(hdr)); #ifdef DIAGNOSTIC if (hdr.type != HCI_SCO_DATA_PKT) { printf("%s: bad SCO packet type\n", unit->hci_devname); goto bad; } if (m->m_pkthdr.len != hdr.length) { printf("%s: bad SCO packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, hdr.length); goto bad; } #endif hdr.con_handle = le16toh(hdr.con_handle); handle = HCI_CON_HANDLE(hdr.con_handle); link = hci_link_lookup_handle(unit, handle); if (link == NULL || link->hl_type == HCI_LINK_ACL) { DPRINTF("%s: dumping packet for unknown handle #%d\n", unit->hci_devname, handle); goto bad; } (*link->hl_sco->sp_proto->input)(link->hl_sco->sp_upper, m); return; bad: m_freem(m); } void hci_sco_start(struct hci_link *link) { } /* * SCO packets have completed at the controller, so we can * signal up to free the buffer space. */ void hci_sco_complete(struct hci_link *link, int num) { DPRINTFN(5, "handle #%d (num=%d)\n", link->hl_handle, num); link->hl_sco->sp_pending--; (*link->hl_sco->sp_proto->complete)(link->hl_sco->sp_upper, num); } /******************************************************************************* * * Generic HCI Connection alloc/free/lookup etc */ struct hci_link * hci_link_alloc(struct hci_unit *unit) { struct hci_link *link; KASSERT(unit); link = malloc(sizeof(struct hci_link), M_BLUETOOTH, M_NOWAIT | M_ZERO); if (link == NULL) return NULL; link->hl_unit = unit; link->hl_state = HCI_LINK_CLOSED; /* init ACL portion */ callout_init(&link->hl_expire); callout_setfunc(&link->hl_expire, hci_acl_timeout, link); TAILQ_INIT(&link->hl_txq); /* outgoing packets */ TAILQ_INIT(&link->hl_reqs); /* request queue */ link->hl_mtu = L2CAP_MTU_DEFAULT; /* L2CAP signal mtu */ link->hl_flush = L2CAP_FLUSH_TIMO_DEFAULT; /* flush timeout */ /* init SCO portion */ MBUFQ_INIT(&link->hl_data); /* attach to unit */ TAILQ_INSERT_HEAD(&unit->hci_links, link, hl_next); return link; } void hci_link_free(struct hci_link *link, int err) { struct l2cap_req *req; struct l2cap_pdu *pdu; struct l2cap_channel *chan; KASSERT(link); DPRINTF("#%d, type = %d, state = %d, refcnt = %d\n", link->hl_handle, link->hl_type, link->hl_state, link->hl_refcnt); /* ACL reference count */ if (link->hl_refcnt > 0) { LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { if (chan->lc_link == link) l2cap_close(chan, err); } } KASSERT(link->hl_refcnt == 0); /* ACL L2CAP requests.. */ while ((req = TAILQ_FIRST(&link->hl_reqs)) != NULL) l2cap_request_free(req); KASSERT(TAILQ_EMPTY(&link->hl_reqs)); /* ACL outgoing data queue */ while ((pdu = TAILQ_FIRST(&link->hl_txq)) != NULL) { TAILQ_REMOVE(&link->hl_txq, pdu, lp_next); MBUFQ_DRAIN(&pdu->lp_data); if (pdu->lp_pending) link->hl_unit->hci_num_acl_pkts += pdu->lp_pending; pool_put(&l2cap_pdu_pool, pdu); } KASSERT(TAILQ_EMPTY(&link->hl_txq)); /* ACL incoming data packet */ if (link->hl_rxp != NULL) { m_freem(link->hl_rxp); link->hl_rxp = NULL; } /* SCO master ACL link */ if (link->hl_link != NULL) { hci_acl_close(link->hl_link, err); link->hl_link = NULL; } /* SCO pcb */ if (link->hl_sco != NULL) { struct sco_pcb *pcb; pcb = link->hl_sco; pcb->sp_link = NULL; link->hl_sco = NULL; (*pcb->sp_proto->disconnected)(pcb->sp_upper, err); } /* flush any SCO data */ MBUFQ_DRAIN(&link->hl_data); /* * Halt the callout - if its already running we cannot free the * link structure but the timeout function will call us back in * any case. */ link->hl_state = HCI_LINK_CLOSED; callout_stop(&link->hl_expire); if (callout_invoking(&link->hl_expire)) return; TAILQ_REMOVE(&link->hl_unit->hci_links, link, hl_next); free(link, M_BLUETOOTH); } /* * Lookup HCI link by address and type. Note that for SCO links there may * be more than one link per address, so we only return links with no * handle (ie new links) */ struct hci_link * hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint16_t type) { struct hci_link *link; KASSERT(unit); KASSERT(bdaddr); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (link->hl_type != type) continue; if (type == HCI_LINK_SCO && link->hl_handle != 0) continue; if (bdaddr_same(&link->hl_bdaddr, bdaddr)) break; } return link; } struct hci_link * hci_link_lookup_handle(struct hci_unit *unit, uint16_t handle) { struct hci_link *link; KASSERT(unit); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (handle == link->hl_handle) break; } return link; } @ 1.1.2.3 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1.2.2 2006/12/30 20:50:32 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.2.2 2006/12/30 20:50:32 yamt Exp $"); d46 1 a47 1 #include a70 1 struct hci_memo *memo; a94 8 memo = hci_memo_find(unit, bdaddr); if (memo != NULL) { cp.page_scan_rep_mode = memo->response.page_scan_rep_mode; cp.page_scan_mode = memo->response.page_scan_mode; cp.clock_offset = htole16(memo->response.clock_offset); } d153 3 a155 8 * Incoming ACL connection. * * For now, we accept all connections but it would be better to check * the L2CAP listen list and only accept when there is a listener * available. * * There should not be a link to the same bdaddr already, we check * anyway though its left unhandled for now. a161 4 link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (link != NULL) return NULL; d203 1 a203 1 if (err) { d205 1 a205 2 err); } d246 1 a246 2 printf("%s: bad ACL packet length (%d != %d)\n", unit->hci_devname, m->m_pkthdr.len, le16toh(hdr.length)); d397 1 d559 1 a559 2 * Incoming SCO Connection. We check the list for anybody willing * to take it. a563 56 struct sockaddr_bt laddr, raddr; struct sco_pcb *pcb, *new; struct hci_link *sco, *acl; memset(&laddr, 0, sizeof(laddr)); laddr.bt_len = sizeof(laddr); laddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&laddr.bt_bdaddr, &unit->hci_bdaddr); memset(&raddr, 0, sizeof(raddr)); raddr.bt_len = sizeof(raddr); raddr.bt_family = AF_BLUETOOTH; bdaddr_copy(&raddr.bt_bdaddr, bdaddr); /* * There should already be an ACL link up and running before * the controller sends us SCO connection requests, but you * never know.. */ acl = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL); if (acl == NULL || acl->hl_state != HCI_LINK_OPEN) return NULL; LIST_FOREACH(pcb, &sco_pcb, sp_next) { if ((pcb->sp_flags & SP_LISTENING) == 0) continue; new = (*pcb->sp_proto->newconn)(pcb->sp_upper, &laddr, &raddr); if (new == NULL) continue; /* * Ok, got new pcb so we can start a new link and fill * in all the details. */ bdaddr_copy(&new->sp_laddr, &unit->hci_bdaddr); bdaddr_copy(&new->sp_raddr, bdaddr); sco = hci_link_alloc(unit); if (sco == NULL) { sco_detach(&new); return NULL; } sco->hl_type = HCI_LINK_SCO; bdaddr_copy(&sco->hl_bdaddr, bdaddr); sco->hl_link = hci_acl_open(unit, bdaddr); KASSERT(sco->hl_link == acl); sco->hl_sco = new; new->sp_link = sco; new->sp_mtu = unit->hci_max_sco_size; return sco; } d676 1 a676 1 struct l2cap_channel *chan, *next; d686 1 a686 3 next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); @ 1.1.2.4 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1.2.3 2007/09/03 14:42:34 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.2.3 2007/09/03 14:42:34 yamt Exp $"); d75 2 a76 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); a116 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: d151 1 a151 1 KASSERT(link != NULL); a213 3 case HCI_LINK_WAIT_AUTH: case HCI_LINK_WAIT_ENCRYPT: case HCI_LINK_WAIT_SECURE: a237 162 * Initiate any Link Mode change requests. */ int hci_acl_setmode(struct hci_link *link) { int err; KASSERT(link != NULL); KASSERT(link->hl_unit != NULL); if (link->hl_state != HCI_LINK_OPEN) return EINPROGRESS; if ((link->hl_flags & HCI_LINK_AUTH_REQ) && !(link->hl_flags & HCI_LINK_AUTH)) { hci_auth_req_cp cp; DPRINTF("requesting auth for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_AUTH; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) && !(link->hl_flags & HCI_LINK_ENCRYPT)) { hci_set_con_encryption_cp cp; /* XXX we should check features for encryption capability */ DPRINTF("requesting encryption for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_ENCRYPT; cp.con_handle = htole16(link->hl_handle); cp.encryption_enable = 0x01; err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { hci_change_con_link_key_cp cp; /* always change link key for SECURE requests */ link->hl_flags &= ~HCI_LINK_SECURE; DPRINTF("changing link key for handle #%d\n", link->hl_handle); link->hl_state = HCI_LINK_WAIT_SECURE; cp.con_handle = htole16(link->hl_handle); err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, &cp, sizeof(cp)); return (err == 0 ? EINPROGRESS : err); } return 0; } /* * Link Mode changed. * * This is called from event handlers when the mode change * is complete. We notify upstream and restart the link. */ void hci_acl_linkmode(struct hci_link *link) { struct l2cap_channel *chan, *next; int err, mode = 0; DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", link->hl_handle, (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); if (link->hl_flags & HCI_LINK_AUTH) mode |= L2CAP_LM_AUTH; if (link->hl_flags & HCI_LINK_ENCRYPT) mode |= L2CAP_LM_ENCRYPT; if (link->hl_flags & HCI_LINK_SECURE) mode |= L2CAP_LM_SECURE; /* * The link state will only be OPEN here if the mode change * was successful. So, we can proceed with L2CAP connections, * or notify already establshed channels, to allow any that * are dissatisfied to disconnect before we restart. */ next = LIST_FIRST(&l2cap_active_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, lc_ncid); if (chan->lc_link != link) continue; switch(chan->lc_state) { case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_close(chan, ECONNABORTED); break; } chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; err = l2cap_send_connect_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ if ((mode & chan->lc_mode) != chan->lc_mode) { l2cap_send_connect_rsp(link, chan->lc_ident, 0, chan->lc_rcid, L2CAP_SECURITY_BLOCK); l2cap_close(chan, ECONNABORTED); break; } l2cap_send_connect_rsp(link, chan->lc_ident, chan->lc_lcid, chan->lc_rcid, L2CAP_SUCCESS); chan->lc_state = L2CAP_WAIT_CONFIG; chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); err = l2cap_send_config_req(chan); if (err) { l2cap_close(chan, err); break; } break; case L2CAP_WAIT_RECV_CONNECT_RSP: case L2CAP_WAIT_CONFIG: case L2CAP_OPEN: /* already established */ (*chan->lc_proto->linkmode)(chan->lc_upper, mode); break; default: break; } } link->hl_state = HCI_LINK_OPEN; hci_acl_start(link); } /* d251 2 a252 2 KASSERT(m != NULL); KASSERT(unit != NULL); d366 2 a367 2 KASSERT(link != NULL); KASSERT(m != NULL); a427 4 * This is called when the queue may need restarting: as new data * is queued, after link mode changes have completed, or when device * buffers have cleared. * d431 3 d444 1 a444 1 KASSERT(link != NULL); d447 1 a447 1 KASSERT(unit != NULL); d655 2 a656 2 KASSERT(m != NULL); KASSERT(unit != NULL); d720 1 a720 1 KASSERT(unit != NULL); d730 1 a730 1 callout_init(&link->hl_expire, 0); d754 1 a754 1 KASSERT(link != NULL); d838 2 a839 2 KASSERT(unit != NULL); KASSERT(bdaddr != NULL); d860 1 a860 1 KASSERT(unit != NULL); @ 1.1.2.5 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1.2.4 2007/10/27 11:36:06 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.2.4 2007/10/27 11:36:06 yamt Exp $"); d99 3 a101 3 cp.page_scan_rep_mode = memo->page_scan_rep_mode; cp.page_scan_mode = memo->page_scan_mode; cp.clock_offset = memo->clock_offset; a992 12 /* * If we made a note of clock offset, keep it in a memo * to facilitate reconnections to this device */ if (link->hl_clock != 0) { struct hci_memo *memo; memo = hci_memo_new(link->hl_unit, &link->hl_bdaddr); if (memo != NULL) memo->clock_offset = link->hl_clock; } @ 1.1.2.6 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1.2.5 2007/11/15 11:45:04 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.2.5 2007/11/15 11:45:04 yamt Exp $"); d428 1 a428 1 aprint_error_dev(unit->hci_dev, "bad ACL packet type\n"); d433 2 a434 3 aprint_error_dev(unit->hci_dev, "bad ACL packet length (%d != %d)\n", m->m_pkthdr.len, le16toh(hdr.length)); d449 1 a449 1 device_xname(unit->hci_dev), handle); d466 2 a467 2 aprint_error_dev(unit->hci_dev, "dropped incomplete ACL packet\n"); d470 3 a472 1 aprint_error_dev(unit->hci_dev, "short ACL packet\n"); d482 2 a483 2 aprint_error_dev(unit->hci_dev, "unexpected packet fragment\n"); d495 3 a497 1 aprint_error_dev(unit->hci_dev, "unknown packet type\n"); d556 1 a556 1 device_xname(link->hl_unit->hci_dev), link->hl_handle, plen, mlen); d709 4 a712 4 aprint_error_dev(link->hl_unit->hci_dev, "%d packets completed on handle #%x but none pending!\n", num, link->hl_handle); d833 1 a833 1 aprint_error_dev(unit->hci_dev, "bad SCO packet type\n"); d838 1 a838 4 aprint_error_dev(unit->hci_dev, "bad SCO packet length (%d != %d)\n", m->m_pkthdr.len, hdr.length); d849 1 a849 1 device_xname(unit->hci_dev), handle); a992 2 callout_destroy(&link->hl_expire); @ 1.1.2.7 log @sync with head. @ text @d1 1 a1 1 /* $NetBSD: hci_link.c,v 1.1.2.6 2008/03/17 09:15:41 yamt Exp $ */ d34 1 a34 1 __KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.1.2.6 2008/03/17 09:15:41 yamt Exp $"); d80 1 a80 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d83 3 a112 1 link->hl_flags |= HCI_LINK_CREATE_CON; d183 1 a183 1 link = hci_link_alloc(unit, bdaddr, HCI_LINK_ACL); d186 2 d788 1 a788 1 sco = hci_link_alloc(unit, bdaddr, HCI_LINK_SCO); d794 3 d885 1 a885 1 hci_link_alloc(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) a895 1 link->hl_type = type; a896 1 bdaddr_copy(&link->hl_bdaddr, bdaddr); d912 1 a912 1 TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next); d1017 1 a1017 1 hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint8_t type) @