2015-05-07 20:25:27

by Ilya Faenson

[permalink] [raw]
Subject: [RFC 0/2] *** Broadcom Bluetooth UART Driver ***

*** Broadcom Bluetooth UART Driver changed as per checkpatch.pl ***

Ilya Faenson (2):
Bluetooth UART Device Tree bindings
Broadcom Bluetooth UART device driver

.../devicetree/bindings/net/bluetooth/btbcm.txt | 54 ++
drivers/bluetooth/Kconfig | 35 +-
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/btbcm.c | 173 ++++-
drivers/bluetooth/btbcm.h | 19 +-
drivers/bluetooth/btbcm_uart.c | 679 +++++++++++++++++
drivers/bluetooth/btbcm_uart.h | 90 +++
drivers/bluetooth/hci_bcm.c | 838 +++++++++++++++++++--
8 files changed, 1799 insertions(+), 90 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/bluetooth/btbcm.txt
create mode 100644 drivers/bluetooth/btbcm_uart.c
create mode 100644 drivers/bluetooth/btbcm_uart.h

--
1.9.1



2015-05-13 21:50:02

by Ilya Faenson

[permalink] [raw]
Subject: RE: [RFC 2/2] Broadcom Bluetooth UART device driver

SGkgTWFyY2VsLA0KDQpJIGFtIGFib3V0IHRvIHByb3ZpZGUgdGhlIG5leHQgcmV2IG9mIHRoZXNl
IHBhdGNoZXMuIEkgYmVsaWV2ZSBJJ3ZlIGFjY291bnRlZCBmb3IgYWxsIHlvdXIgY29tbWVudHMg
ZXhjZXB0IGZvciB0aGUgVUFSVCBhY3Rpb25zIGN1cnJlbnRseSBkb25lIGluIHRoZSBCcm9hZGNv
bSBzcGVjaWZpYyBjb2RlLiBTaW5jZSB5b3UgY2xlYXJseSBwcmVmZXIgSW50ZWwncyBhcHByb2Fj
aCBhbmQgaGF2ZSByZWZlcnJlZCBtZSB0byB0aGUgSW50ZWwgY29kZSB0byB0aGF0IGV4dGVudCwg
Y291bGQgeW91IHBsZWFzZSBhY2NlcHQgcmVsZXZhbnQgSW50ZWwncyBwYXRjaGVzIHNvIEkgY2Fu
IHVzZSB0aGUgZW5oYW5jZWQgbGluZSBkaXNjaXBsaW5lIHdpdGggdGhlIGJhdWQgcmF0ZSBzdXBw
b3J0IGFzIG9wcG9zZWQgdG8gY29kaW5nIHRoYXQgZnJvbSBzY3JhdGNoIGFnYWluLiBJIHdpbGwg
dGhlbiBpbnRlZ3JhdGUgZXZlcnl0aGluZyBhbmQgaXNzdWUgYW5vdGhlciBzZXJpZXMgb2YgQnJv
YWRjb20gQlQgVUFSVCBwYXRjaGVzLg0KDQpQbGVhc2Ugc2VlIG15IHJlc3BvbnNlcyBpbmxpbmUg
bWFya2VkIGJ5IHRoZSAiSUY6IiBwcmVmaXguDQoNClRoYW5rcywNCiAtSWx5YQ0KDQotLS0tLU9y
aWdpbmFsIE1lc3NhZ2UtLS0tLQ0KRnJvbTogTWFyY2VsIEhvbHRtYW5uIFttYWlsdG86bWFyY2Vs
QGhvbHRtYW5uLm9yZ10gDQpTZW50OiBNb25kYXksIE1heSAxMSwgMjAxNSAzOjA0IFBNDQpUbzog
SWx5YSBGYWVuc29uDQpDYzogbGludXgtYmx1ZXRvb3RoQHZnZXIua2VybmVsLm9yZw0KU3ViamVj
dDogUmU6IFtSRkMgMi8yXSBCcm9hZGNvbSBCbHVldG9vdGggVUFSVCBkZXZpY2UgZHJpdmVyDQoN
CkhpIElseWEsDQoNCj4gVGhpcyBjb2RlIGltcGxlbWVudHMgdGhlIEJyb2FkY29tIEJsdWV0b290
aCBkZXZpY2UgZHJpdmVyLg0KPiBJdCBtYW5hZ2VzIGRldmljZSBjb25maWd1cmF0aW9uLCBmaXJt
d2FyZSBkb3dubG9hZCBhbmQNCj4gcG93ZXIgbWFuYWdlbWVudC4NCj4gDQo+IFNpZ25lZC1vZmYt
Ynk6IElseWEgRmFlbnNvbiA8aWZhZW5zb25AYnJvYWRjb20uY29tPg0KPiAtLS0NCj4gZHJpdmVy
cy9ibHVldG9vdGgvS2NvbmZpZyAgICAgIHwgIDM1ICstDQo+IGRyaXZlcnMvYmx1ZXRvb3RoL01h
a2VmaWxlICAgICB8ICAgMSArDQo+IGRyaXZlcnMvYmx1ZXRvb3RoL2J0YmNtLmMgICAgICB8IDE3
MyArKysrKysrKy0NCj4gZHJpdmVycy9ibHVldG9vdGgvYnRiY20uaCAgICAgIHwgIDE5ICstDQo+
IGRyaXZlcnMvYmx1ZXRvb3RoL2J0YmNtX3VhcnQuYyB8IDY3OSArKysrKysrKysrKysrKysrKysr
KysrKysrKysrKysrKysNCj4gZHJpdmVycy9ibHVldG9vdGgvYnRiY21fdWFydC5oIHwgIDkwICsr
KysrDQo+IGRyaXZlcnMvYmx1ZXRvb3RoL2hjaV9iY20uYyAgICB8IDgzOCArKysrKysrKysrKysr
KysrKysrKysrKysrKysrKysrKysrKysrLS0tLQ0KPiA3IGZpbGVzIGNoYW5nZWQsIDE3NDUgaW5z
ZXJ0aW9ucygrKSwgOTAgZGVsZXRpb25zKC0pDQo+IGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJz
L2JsdWV0b290aC9idGJjbV91YXJ0LmMNCj4gY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvYmx1
ZXRvb3RoL2J0YmNtX3VhcnQuaA0KPiANCj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvYmx1ZXRvb3Ro
L0tjb25maWcgYi9kcml2ZXJzL2JsdWV0b290aC9LY29uZmlnDQo+IGluZGV4IGVkNWMyNzMuLjFi
ZmUzNmIgMTAwNjQ0DQo+IC0tLSBhL2RyaXZlcnMvYmx1ZXRvb3RoL0tjb25maWcNCj4gKysrIGIv
ZHJpdmVycy9ibHVldG9vdGgvS2NvbmZpZw0KPiBAQCAtOCw2ICs4LDEyIEBAIGNvbmZpZyBCVF9J
TlRFTA0KPiBjb25maWcgQlRfQkNNDQo+IAl0cmlzdGF0ZQ0KPiAJc2VsZWN0IEZXX0xPQURFUg0K
PiArCWhlbHANCj4gKwkgIFRoaXMgZmVhdHVyZSBpcyByZXF1aXJlZCBpZiB5b3Ugd2FudCB0byB1
c2UgQnJvYWRjb20gQmx1ZXRvb3RoDQo+ICsJICBvdmVyIGJvdGggVVNCIGFuZCBVQVJULg0KDQpJ
IHRoaW5rIHRoaXMgc2hvdWxkIHNheSDigJxvdmVyIGVpdGhlciBVU0Igb3IgVUFSVOKAnS4NCg0K
SUY6IENoYW5nZWQuDQoNCj4gKw0KPiArCSAgU2F5IFkgaGVyZSB0byBjb21waWxlIHN1cHBvcnQg
Zm9yIEJyb2FkY29tIEJsdWV0b290aA0KPiArCSAga2VybmVsIG9yIHNheSBNIHRvIGNvbXBpbGUg
aXQgYXMgbW9kdWxlIChidHVzYikuDQoNClRoZSByZWFzb24gd2h5IEkgbmV2ZXIgYm90aGVyZWQg
YWRkaW5nIGEgaGVscCB0ZXh0IGRlc2NyaXB0aW9uIHRvIHRoaXMgZW50cnkgaXMgYmVjYXVzZSBp
dCBpcyBhIGhpZGRlbiBlbnRyeS4gU2VsZWN0aW5nIGVpdGhlciB0aGUgQnJvYWRjb20gVVNCIG9y
IGl0cyBVQVJUIHZlcnNpb24gY2F1c2VzIHRoaXMgbW9kdWxlIHRvIGJlIGJ1aWx0LiBZb3Ugd2ls
bCBuZXZlciBzZWUgdGhpcyBlbnRyeSBpbiB0aGUga2VybmVsIGNvbmZpZ3VyYXRpb24gdXNlciBp
bnRlcmZhY2UuDQoNClRoYXQgbWVhbnMgeW91IGNhbiBub3QgYWN0dWFsbHkgc2VsZWN0IFkgb3Ig
TSBvciBOIGhlcmUuIEl0IGlzIHNlbGVjdGVkIGZvciB5b3UgYmFzZWQgb24gb3RoZXIgY2hvaWNl
cyBtYWRlIGJ5IGFjdHVhbCBleHBvc2VkIG9wdGlvbnMuIFNvIGl0IGlzIHVwIHRvIHlvdSBpZiB5
b3Ugd2FudCB0byBrZWVwIHRoaXMgb3Igbm90LiBJZiB5b3UgZG8sIHRoZW4gdGhpcyBzaG91bGQg
YmUgYSBzZXBhcmF0ZSBwYXRjaCBzaW5jZSBpdCBpcyBmaXhpbmcgc29tZSBleGlzdGluZyBwaWVj
ZS4gQW5kIHlvdSBtaWdodCBhbHNvIHdhbnQgdG8gZml4IHVwIHRoZSBCVF9JTlRFTCBvbmUgc2lu
Y2UgdGhhdCBoYXMgdGhlIHNhbWUg4oCccHJvYmxlbeKAnS4gSWYgd2UgZml4IHNvbWV0aGluZywg
dGhlbiB3ZSBmaXggaXQgZm9yIGFsbCBjdXJyZW50IGV4aXN0ZW5jZXMuDQoNCkFuZCBzL2J0dXNi
L2J0YmNtLyBpbiBjYXNlIHlvdSBvdmVybG9va2VkIHRoYXQgc21hbGwgY29weSZwYXN0ZSBidWcu
DQoNCklGOiBSZW1vdmVkLiBJIGFkZGVkIHRoYXQgaGVscCBiZWNhdXNlIHRoZSBjaGVja3BhdGNo
IHNjcmlwdCB3YXMgY29tcGxhaW5pbmcgYWJvdXQgdGhlIGxhY2sgb2YgaXQuDQoNCj4gDQo+IGNv
bmZpZyBCVF9IQ0lCVFVTQg0KPiAJdHJpc3RhdGUgIkhDSSBVU0IgZHJpdmVyIg0KPiBAQCAtMjIs
MTUgKzI4LDE2IEBAIGNvbmZpZyBCVF9IQ0lCVFVTQg0KPiAJICBrZXJuZWwgb3Igc2F5IE0gdG8g
Y29tcGlsZSBpdCBhcyBtb2R1bGUgKGJ0dXNiKS4NCj4gDQo+IGNvbmZpZyBCVF9IQ0lCVFVTQl9C
Q00NCj4gLQlib29sICJCcm9hZGNvbSBwcm90b2NvbCBzdXBwb3J0Ig0KPiArCWJvb2wgIkJyb2Fk
Y29tIFVTQiBzdXBwb3J04oCdDQoNCkkgcHJlZmVycmVkIOKAnHByb3RvY29s4oCdIG92ZXIg4oCc
VVNC4oCdIGhlcmUgd2hlbiBJIGFkZGVkIGl0IGZvciB0aGUgc2ltcGxlIHJlYXNvbiBzaW5jZSB0
aGlzIGlzIHRoZSBHZW5lcmljIFVTQiBkcml2ZXIuIE5vIG5lZWQgdG8gcmVwZWF0IHRoZSBVU0Ig
cGFydCBvZiBpdC4gVGhhdCBpcyBwcmV0dHkgb2J2aW91cy4NCg0KSUY6IFJlc3RvcmVkIHRoZSBv
cmlnaW5hbCB3b3JkaW5nLg0KDQo+IAlkZXBlbmRzIG9uIEJUX0hDSUJUVVNCDQo+IAlzZWxlY3Qg
QlRfQkNNDQo+IAlkZWZhdWx0IHkNCj4gCWhlbHANCj4gLQkgIFRoZSBCcm9hZGNvbSBwcm90b2Nv
bCBzdXBwb3J0IGVuYWJsZXMgZmlybXdhcmUgYW5kIHBhdGNocmFtDQo+IC0JICBkb3dubG9hZCBz
dXBwb3J0IGZvciBCcm9hZGNvbSBCbHVldG9vdGggY29udHJvbGxlcnMuDQo+ICsJICBCcm9hZGNv
bSBCbHVldG9vdGggVVNCIGRyaXZlciBzdXBwb3J0Lg0KPiArCSAgVGhlIEJyb2FkY29tIFVTQiBz
dXBwb3J0IGVuYWJsZXMgZmlybXdhcmUgYW5kIHBhdGNocmFtDQo+ICsJICBkb3dubG9hZCBmb3Ig
QnJvYWRjb20gQmx1ZXRvb3RoIFVTQiBjb250cm9sbGVycy4NCj4gDQo+IC0JICBTYXkgWSBoZXJl
IHRvIGNvbXBpbGUgc3VwcG9ydCBmb3IgQnJvYWRjb20gcHJvdG9jb2wuDQo+ICsJICBTYXkgWSBo
ZXJlIHRvIGNvbXBpbGUgc3VwcG9ydCBmb3IgQnJvYWRjb20gVVNCLg0KDQpJIGFtIG5vdCBjb252
aW5jZWQgdGhhdCB0aGlzIG1ha2VzIGl0IGNsZWFyZXIuIEl0IGlzIGFuIG9wdGlvbiBvZiB0aGUg
VVNCIGRyaXZlci4gSXQgaXMgbm90IGEgZGlmZmVyZW50IGRyaXZlci4gSXQgaXMgc3RpbGwgdGhl
IHNhbWUgZHJpdmVyLiBJdCBpcyBqdXN0IGVuYWJsaW5nIHRoZSBzdXBwb3J0IGZvciBCcm9hZGNv
bSBiYXNlZCBkZXZpY2VzLg0KDQpJRjogUmVzdG9yZWQgdGhlIG9yaWdpbmFsIHdvcmRpbmcuDQoN
Cj4gDQo+IGNvbmZpZyBCVF9IQ0lCVFNESU8NCj4gCXRyaXN0YXRlICJIQ0kgU0RJTyBkcml2ZXIi
DQo+IEBAIC0xMjUsMTUgKzEzMiwyNSBAQCBjb25maWcgQlRfSENJVUFSVF9JTlRFTA0KPiAJICBT
YXkgWSBoZXJlIHRvIGNvbXBpbGUgc3VwcG9ydCBmb3IgSW50ZWwgcHJvdG9jb2wuDQo+IA0KPiBj
b25maWcgQlRfSENJVUFSVF9CQ00NCj4gLQlib29sICJCcm9hZGNvbSBwcm90b2NvbCBzdXBwb3J0
Ig0KPiAtCWRlcGVuZHMgb24gQlRfSENJVUFSVA0KPiArCWJvb2wgIkJyb2FkY29tIEJUIFVBUlQg
c2VyaWFsIHN1cHBvcnTigJ0NCg0KU2FtZSBleHBsYW5hdGlvbiBhcyBhYm92ZS4gSXQgaXMgYW4g
b3B0aW9uIG9mIHRoZSBVQVJUIGRyaXZlci4gTm8gbmVlZCB0byByZXBlYXQgdGhhdCBpbmZvcm1h
dGlvbi4gVGhhdCBpcyB3aHkgSSBjaG9vc2UgdG8gdXNlIHRoZSB3b3JkIOKAnHByb3RvY29s4oCd
IGluIHRoZSBmaXJzdCBwbGFjZS4NCg0KSUY6IFJlc3RvcmVkIHRoZSBvcmlnaW5hbCB3b3JkaW5n
Lg0KDQo+IAlzZWxlY3QgQlRfSENJVUFSVF9INA0KPiArCXNlbGVjdCBCVF9VQVJUX0JDTQ0KPiAJ
c2VsZWN0IEJUX0JDTQ0KPiAJaGVscA0KPiAtCSAgVGhlIEJyb2FkY29tIHByb3RvY29sIHN1cHBv
cnQgZW5hYmxlcyBCbHVldG9vdGggSENJIG92ZXIgc2VyaWFsDQo+IC0JICBwb3J0IGludGVyZmFj
ZSBmb3IgQnJvYWRjb20gQmx1ZXRvb3RoIGNvbnRyb2xsZXJzLg0KPiArCSAgSENJX1VBUlRfQkNN
IGlzIGEgcHJvdG9jb2wgZm9yIGluaXRpYWxpemluZywgbWFuYWdpbmcgYW5kDQo+ICsJICBjb21t
dW5pY2F0aW5nIHdpdGggQnJvYWRjb20gVUFSVCBCbHVldG9vdGggZGV2aWNlcy4NCj4gKwkgIFRo
aXMgcHJvdG9jb2wgaW5pdGlhbGl6ZXMgY2hpcHMgYW5kIHBvd2VyLW1hbmFnZXMgdGhlbS4NCj4g
KwkgIEVuYWJsZSB0aGlzIGlmIHlvdSBoYXZlIHNlcmlhbCBCcm9hZGNvbSBCbHVldG9vdGggZGV2
aWNlLg0KPiArDQo+ICsJICBTYXkgWSBoZXJlIHRvIGNvbXBpbGUgc3VwcG9ydCBmb3IgQnJvYWRj
b20gVUFSVCBwcm90b2NvbC4NCj4gKw0KPiArY29uZmlnIEJUX1VBUlRfQkNNDQo+ICsJdHJpc3Rh
dGUgIkJyb2FkY29tIEJUIFVBUlQgZHJpdmVyIg0KPiArCWRlcGVuZHMgb24gQlRfSENJVUFSVF9I
NCAmJiBUVFkNCj4gKwloZWxwDQo+ICsJICBUaGlzIGRyaXZlciBzdXBwb3J0cyB0aGUgSENJX1VB
UlRfQlQgcHJvdG9jb2wuDQo+IA0KPiAtCSAgU2F5IFkgaGVyZSB0byBjb21waWxlIHN1cHBvcnQg
Zm9yIEJyb2FkY29tIHByb3RvY29sLg0KPiArCSAgSXQgbWFuYWdlcyBCbHVldG9vdGggVUFSVCBk
ZXZpY2UgcHJvcGVydGllcyBhbmQgR1BJT3MuDQoNCk15IGlkZWEgd2FzIGFjdHVhbGx5IG5vdCBw
dXR0aW5nIHRoaXMgaW50byBhIHNlcGFyYXRlIGtlcm5lbCBtb2R1bGUuIFNvIEkgaGF2ZSB0byBy
ZWFkIHRocm91Z2ggdGhpcyB3aG9sZSBwYXRjaCBzZXQgdG8gc2VlIGlmIHRoYXQgbWFrZXMgc2Vu
c2Ugb3Igbm90LiBNeSBpbml0aWFsIHRob3VnaHQgaGFzIGJlZW4gdGhhdCB0aGlzIGlzIGluIHRo
ZSBkcml2ZXJzL2JsdWV0b290aC9oY2lfYmNtLmMuDQoNCkp1c3QgYXMgYSBub3RlLCBpbiBjYXNl
IG9mIGEgc2VwYXJhdGUgZHJpdmVyLCB0aGF0IGRyaXZlciBzaG91bGQgaGF2ZSBiZWVuIGluIGl0
cyBvd24gcGF0Y2ggYW5kIG5vdCBpbnRlcm1peGVkIHdpdGggdGhpcyBvbmUuIE1peGluZyB0aGVt
IHRvZ2V0aGVyIG1ha2VzIGl0IHJlYWxseSBoYXJkIHRvIHJldmlldyBpdC4NCg0KSUY6IFRoaXMg
ZHJpdmVyIGlzIGluc3RhbnRpYXRlZCBieSB0aGUgRGV2aWNlIFRyZWUgb3IgaXQgY2FuJ3QgZWFz
aWx5IHNoYXJlIGEgbW9kdWxlIHdpdGggdGhlIEJsdWVaIHByb3RvY29sLiBXaWxsIHN1Ym1pdCBp
dCBub3cgYXMgYSBzZXBhcmF0ZSBwYXRjaC4NCg0KPiANCj4gY29uZmlnIEJUX0hDSUJDTTIwM1gN
Cj4gCXRyaXN0YXRlICJIQ0kgQkNNMjAzeCBVU0IgZHJpdmVyIg0KPiBkaWZmIC0tZ2l0IGEvZHJp
dmVycy9ibHVldG9vdGgvTWFrZWZpbGUgYi9kcml2ZXJzL2JsdWV0b290aC9NYWtlZmlsZQ0KPiBp
bmRleCBkZDBkOWM0Li4wZTVmZDY2IDEwMDY0NA0KPiAtLS0gYS9kcml2ZXJzL2JsdWV0b290aC9N
YWtlZmlsZQ0KPiArKysgYi9kcml2ZXJzL2JsdWV0b290aC9NYWtlZmlsZQ0KPiBAQCAtMjEsNiAr
MjEsNyBAQCBvYmotJChDT05GSUdfQlRfTVJWTCkJCSs9IGJ0bXJ2bC5vDQo+IG9iai0kKENPTkZJ
R19CVF9NUlZMX1NESU8pCSs9IGJ0bXJ2bF9zZGlvLm8NCj4gb2JqLSQoQ09ORklHX0JUX1dJTElO
SykJCSs9IGJ0d2lsaW5rLm8NCj4gb2JqLSQoQ09ORklHX0JUX0JDTSkJCSs9IGJ0YmNtLm8NCj4g
K29iai0kKENPTkZJR19CVF9VQVJUX0JDTSkJKz0gYnRiY21fdWFydC5vDQo+IA0KPiBidG1ydmwt
eQkJCTo9IGJ0bXJ2bF9tYWluLm8NCj4gYnRtcnZsLSQoQ09ORklHX0RFQlVHX0ZTKQkrPSBidG1y
dmxfZGVidWdmcy5vDQo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2JsdWV0b290aC9idGJjbS5jIGIv
ZHJpdmVycy9ibHVldG9vdGgvYnRiY20uYw0KPiBpbmRleCA0YmJhODY2Li4zZTlhYzMwIDEwMDY0
NA0KPiAtLS0gYS9kcml2ZXJzL2JsdWV0b290aC9idGJjbS5jDQo+ICsrKyBiL2RyaXZlcnMvYmx1
ZXRvb3RoL2J0YmNtLmMNCj4gQEAgLTIsNyArMiw4IEBADQo+ICAqDQo+ICAqICBCbHVldG9vdGgg
c3VwcG9ydCBmb3IgQnJvYWRjb20gZGV2aWNlcw0KPiAgKg0KPiAtICogIENvcHlyaWdodCAoQykg
MjAxNSAgSW50ZWwgQ29ycG9yYXRpb24NCj4gKyAqICBDb3B5cmlnaHQgKEMpIDIwMTUgSW50ZWwg
Q29ycG9yYXRpb24NCj4gKyAqICBDb3B5cmlnaHQgKEMpIDIwMTUgQnJvYWRjb20gQ29ycG9yYXRp
b24NCg0KSnVzdCBmb2xsb3cgdGhlIGRvdWJsZSBzcGFjZXMgdGhhdCB3ZSBub3JtYWxseSBkbyBm
b3Igc2VwYXJhdGluZyB0aGUgeWVhciBmcm9tIHRoZSBjb21wYW55Lg0KDQpJRjogT2theS4NCg0K
PiAgKg0KPiAgKg0KPiAgKiAgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4g
cmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkNCj4gQEAgLTE1LDI0ICsxNiwyMiBAQA0KPiAg
KiAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAg
U2VlIHRoZQ0KPiAgKiAgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWls
cy4NCj4gICoNCj4gLSAqICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBH
TlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KPiAtICogIGFsb25nIHdpdGggdGhpcyBwcm9ncmFt
OyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlDQo+IC0gKiAgRm91bmRhdGlvbiwg
SW5jLiwgNTkgVGVtcGxlIFBsYWNlLCBTdWl0ZSAzMzAsIEJvc3RvbiwgTUEgIDAyMTExLTEzMDcg
IFVTQQ0KPiAtICoNCg0KSSBrbm93IHRoYXQgZXZlbnR1YWxseSB3ZSBoYXZlIHRvIHJlbW92ZSB0
aGUgRlNGIGFkZHJlc3MgZnJvbSB0aGUgY29kZSwgYnV0IHRoYXQgaXMgY2xlYXJseSBhbiBpbmRp
dmlkdWFsIGNsZWFudXAgcGF0Y2ggdGhhdCBjYW4gY29tZSBsYXRlci4gTWl4aW5nIHRoZXNlIGlu
IGVhcmx5IG9uIGp1c3QgYWRkcyBjbHV0dGVyIGZvciB0aGUgcmV2aWV3Lg0KDQpJRjogT2theSwg
cmVzdG9yZWQuDQoNCj4gICovDQo+IA0KPiAjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+DQo+ICNp
bmNsdWRlIDxsaW51eC9maXJtd2FyZS5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L3R0eS5oPg0KPiAj
aW5jbHVkZSA8YXNtL3VuYWxpZ25lZC5oPg0KPiANCj4gI2luY2x1ZGUgPG5ldC9ibHVldG9vdGgv
Ymx1ZXRvb3RoLmg+DQo+ICNpbmNsdWRlIDxuZXQvYmx1ZXRvb3RoL2hjaV9jb3JlLmg+DQo+IA0K
PiArI2luY2x1ZGUg4oCcaGNpX3VhcnQuaCINCg0KVGhpcyBzY3JlYW1zIGxheWVyIHZpb2xhdGlv
bi4NCg0KYnRiY20uW2NoXSBzaG91bGQgYmUgaW5kZXBlbmRlbnQgc28gdGhhdCB0aGUgVVNCIGFu
ZCBhbHNvIHRoZSBVQVJUIGRyaXZlciBjYW4gdXRpbGlzZSBpdC4NCg0KSUY6IFlvdSBoYXZlIG5v
dCBjb21wbGFpbmVkIHdoZW4gSW50ZWwgaW5jbHVkZWQgdGhlIHNhbWUgaGVhZGVyIGludG8gdGhl
IHNhbWUgZmlsZS4gQ291bGQgeW91IHBsZWFzZSBhcHByb3ZlIHRoZSByZWxldmFudCBJbnRlbCBw
YXRjaGVzIHNvIHdlIGNhbiB1c2UgdGhlIGxpbmUgZGlzY2lwbGluZSBhbmQgb3RoZXIgcmVsZXZh
bnQgY2hhbmdlcyB3aXRob3V0IHJlLWNyZWF0aW5nIHRoZSBzYW1lIHRoaW5ncyBmcm9tIHNjcmF0
Y2ggYW5kIGRvaW5nIHRoZSBzdWJzZXF1ZW50IG1lcmdlcz8NCg0KQWxzbyBjaGFuZ2VzIHRvIGJ0
YmNtLltjaF0gc2hvdWxkIGNvbWUgYXMgc2VwYXJhdGUgcGF0Y2hlcy4gVGhleSBzaG91bGQgY29t
ZSBmaXJzdCBhbmQgaW50cm9kdWNlIG5ldyBjb21tb24gdXNlZCBmdW5jdGlvbmFsaXR5Lg0KDQpJ
RjogU3RydWN0dXJlZCBhcyBhIHNlcGFyYXRlIHBhdGNoLg0KDQo+ICNpbmNsdWRlICJidGJjbS5o
Ig0KPiANCj4gLSNkZWZpbmUgVkVSU0lPTiAiMC4xIg0KPiArI2RlZmluZSBWRVJTSU9OICIwLjIi
DQo+IA0KPiAtI2RlZmluZSBCREFERFJfQkNNMjA3MDJBMCAoJihiZGFkZHJfdCkge3sweDAwLCAw
eGEwLCAweDAyLCAweDcwLCAweDIwLCAweDAwfX0pDQo+ICsjZGVmaW5lIEJEQUREUl9CQ00yMDcw
MkEwICgmKGJkYWRkcl90KSB7ezB4MDAsIDB4YTAsIDB4MDIsIDB4NzAsIDB4MjAsIDB4MDB9IH0p
DQoNClJlbW92ZSB0aGlzIG9uZS4gVGhhdCB3aGl0ZXNwYWNlIGZpeCB5b3UgYXJlIGRvaW5nIGhl
cmUgaXMgd3JvbmcuDQoNCklGOiBSZW1vdmVkLg0KDQo+IA0KPiBpbnQgYnRiY21fY2hlY2tfYmRh
ZGRyKHN0cnVjdCBoY2lfZGV2ICpoZGV2KQ0KPiB7DQo+IEBAIC00Myw2ICs0Miw3IEBAIGludCBi
dGJjbV9jaGVja19iZGFkZHIoc3RydWN0IGhjaV9kZXYgKmhkZXYpDQo+IAkJCSAgICAgSENJX0lO
SVRfVElNRU9VVCk7DQo+IAlpZiAoSVNfRVJSKHNrYikpIHsNCj4gCQlpbnQgZXJyID0gUFRSX0VS
Uihza2IpOw0KPiArDQoNCklmIHlvdSB3YW50IHRvIGZpeCBzb21lIGNvZGluZyBzdHlsZSwgdGhl
biBwbGVhc2UgZG8gdGhhdCBpbiBhIHNlcGFyYXRlIHBhdGNoLiBJZiB0aGV5IGFyZSB2YWxpZCBm
aXhlcywgdGhlbiB0aGV5IGFyZSBlYXN5IHRvIGFwcGx5LiBJbnRlcm1peGVkIHdpdGggYWRkaW5n
IGZlYXR1cmVzIGl0IGlzIGEgYmFkIGlkZWEuDQoNCklGOiBBbGwgdGhlc2Ugd2VyZSBjYXVzZWQg
YnkgdGhlIGNoZWNrcGF0Y2ggc2NyaXB0IGZpeGVzLiBUaGUgZmlyc3QgUkZDIGNvbW1lbnQgYXNr
ZWQgbWUgdG8gZml4IHRoZW0uIEkndmUgbm93IHJlbW92ZWQgdGhhdCBjYXVzaW5nIHRoZSBjaGVj
a3BhdGNoIGlzc3VlcyBhZ2Fpbi4NCg0KPiAJCUJUX0VSUigiJXM6IEJDTTogUmVhZGluZyBkZXZp
Y2UgYWRkcmVzcyBmYWlsZWQgKCVkKSIsDQo+IAkJICAgICAgIGhkZXYtPm5hbWUsIGVycik7DQo+
IAkJcmV0dXJuIGVycjsNCj4gQEAgLTE1OSw2ICsxNTksOCBAQCBpbnQgYnRiY21fcGF0Y2hyYW0o
c3RydWN0IGhjaV9kZXYgKmhkZXYsIGNvbnN0IGNoYXIgKmZpcm13YXJlKQ0KPiAJfQ0KPiANCj4g
CS8qIDI1MCBtc2VjIGRlbGF5IGFmdGVyIExhdW5jaCBSYW0gY29tcGxldGVzICovDQo+ICsJQlRf
SU5GTygiJXM6IEJDTTogRGVsYXlpbmcgdXBvbiB0aGUgcGF0Y2ggZG93bmxvYWQgY29tcGxldGlv
bi4uLiIsDQo+ICsJCWhkZXYtPm5hbWUpOw0KDQpTYW1lIG9uIHRoaXMgb25lLiBJZiB0aGlzIGFk
ZHMgdmFsdWUsIHBsZWFzZSBzcGxpdCB0aGlzIG91dCBhbmQgaGF2ZSBhIHNlcGFyYXRlIHBhdGNo
IGZvciBpdC4gSSBrbm93IHRoaXMgbWlnaHQgc291bmQgYSBiaXQgbGlrZSBleHRyYSBvdmVyaGVh
ZCwgYnV0IHdoZW4gaGF2aW5nIHRvIGxvb2sgYmFjayBhdCB0aGlzIGluIDIgeWVhcnMgaXQgaGVs
cHMgYSBsb29rIHRvIHNlZSB0aGUgcmVhc29uaW5nIGZvciBpdC4gU28gZmFyIHdlIGhhdmUgZ290
dGVuIGF3YXkgd2l0aCBub3QgcHJpbnRpbmcgdGhpcyBpbmZvcm1hdGlvbiBhbmQgbm9ib2R5IGNv
bXBsYWluZWQuIElmIHRoaXMgaXMganVzdCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLCB0aGVuIGtl
ZXBpbmcgaXQgYXMgYSBzZXBhcmF0ZSBwYXRjaCBpcyBiZXR0ZXIuIFlvdSBjYW4ganVzdCBkcm9w
IGl0IGF0IHRoZSBlbmQuDQoNCklGOiBFYXNpZXIgdG8gcmVtb3ZlIHRoYW4gZGlzY3Vzcy4NCg0K
PiAJbXNsZWVwKDI1MCk7DQo+IA0KPiBkb25lOg0KPiBAQCAtMTc0LDYgKzE3Niw3IEBAIHN0YXRp
YyBpbnQgYnRiY21fcmVzZXQoc3RydWN0IGhjaV9kZXYgKmhkZXYpDQo+IAlza2IgPSBfX2hjaV9j
bWRfc3luYyhoZGV2LCBIQ0lfT1BfUkVTRVQsIDAsIE5VTEwsIEhDSV9JTklUX1RJTUVPVVQpOw0K
PiAJaWYgKElTX0VSUihza2IpKSB7DQo+IAkJaW50IGVyciA9IFBUUl9FUlIoc2tiKTsNCj4gKw0K
PiAJCUJUX0VSUigiJXM6IEJDTTogUmVzZXQgZmFpbGVkICglZCkiLCBoZGV2LT5uYW1lLCBlcnIp
Ow0KPiAJCXJldHVybiBlcnI7DQo+IAl9DQo+IEBAIC0yNDYsOCArMjQ5LDEwIEBAIHN0YXRpYyBz
dHJ1Y3Qgc2tfYnVmZiAqYnRiY21fcmVhZF91c2JfcHJvZHVjdChzdHJ1Y3QgaGNpX2RldiAqaGRl
dikNCj4gc3RhdGljIGNvbnN0IHN0cnVjdCB7DQo+IAl1MTYgc3VidmVyOw0KPiAJY29uc3QgY2hh
ciAqbmFtZTsNCj4gKwl1MzIgYmF1ZF9yYXRlOwkvKiBvcGVyYXRpb25hbCBiYXVkIHJhdGUgKi8N
Cj4gfSBiY21fdWFydF9zdWJ2ZXJfdGFibGVbXSA9IHsNCj4gLQl7IDB4NDEwZSwgIkJDTTQzMzQx
QjAiCX0sCS8qIDAwMi4wMDEuMDE0ICovDQo+ICsJeyAweDQxMGUsICJCQ000MzM0MUIwIiwgMzAw
MDAwMH0sICAgICAgICAgICAgICAgICAgICAvKiAwMDIuMDAxLjAxNCAqLw0KPiArCXsgMHg2MTBj
LCAiQkNNNDM1NF8wMDMuMDAxLjAxMi4wMzA2LjA2NTkiLCAzMDAwMDAwfSwgLyogMDAzLjAwMS4w
MTIgKi8NCg0KVGhlc2UgYXJlIG5vdCBmaXJtd2FyZSBuYW1lcy4gSXQgaXMganVzdCBzdXBwb3Nl
IHRvIHRoZSB0aGUgZmlyc3QgcGFydCBvZiB0aGUgc3RyaW5nLiBTbyBqdXN0IGFkZGluZyBmaXJt
d2FyZSBmaWxlbmFtZXMgaGVyZSBpcyB0aGUgd3JvbmcgYXBwcm9hY2guDQoNCkFuZCBhcyB3ZSB0
YWxrZWQsIHNvb24sIHRoaXMgbmVlZHMgdG8gYmUgcmVwbGFjZWQgYnkgYSDigJxtYW5pZmVzdOKA
nSBmaWxlIHRoYXQgd2lsbCBiZSBsb2FkZWQgdmlhIHJlcXVlc3RfZmlybXdhcmUoKSBmcm9tIHRo
ZSBmaXJtd2FyZSBkaXJlY3RvcnkgdGhhdCBhbGxvd3MgdG8gbWFrZSB0aGUgcmlnaHQgc2VsZWN0
aW9uIG9mIGZpcm13YXJlIGZpbGUuIFdlIGFyZSBqdXN0IG5vdCB0aGVyZSB5ZXQuIFNvIGl0IHdp
bGwgYmUgZWFzeSB0byBqdXN0IGFkZCB0aGUgbmV3IHN1YnZlciBlbnRyeSBoZXJlIGFzIGl0IGlz
Lg0KDQpBbHNvIHRoaXMgdGFibGUgaXMgbm90IGFwcHJvcHJpYXRlIGZvciB0aGUgdGhlIG9wZXJh
dGlvbmFsIGJhdWQgcmF0ZS4gSSBwcmVmZXIgd2UgZG8gdGhhdCBleGFjdGx5IGFzIEZyZWQgZGlk
IGluIGhpcyBwYXRjaGVzIGFuZCB0aGVuIG1heWJlIGxhdGVyIGFkZCBhIGZlYXR1cmUgdG8gb3Zl
cndyaXRlIHRoaXMgdmlhIEFDUEkgb3IgRFRTIGVudHJpZXMuDQoNCklGOiBQbGVhc2UgYXBwcm92
ZSBGcmVkJ3MgcGF0Y2hlcyB0aGVuLg0KDQo+IAl7IH0NCj4gfTsNCj4gDQo+IEBAIC0yNjgsNiAr
MjczLDEzMyBAQCBzdGF0aWMgY29uc3Qgc3RydWN0IHsNCj4gCXsgfQ0KPiB9Ow0KPiANCj4gKy8q
DQo+ICsgKiBTZXQgdGhlIFVBUlQgaW50byB0aGUgZGVmYXVsdHMNCj4gKyAqLw0KPiAraW50IGJ0
YmNtX2luaXRfdWFydChzdHJ1Y3QgaGNpX3VhcnQgKmh1KQ0KPiArew0KPiArCXN0cnVjdCBrdGVy
bWlvcyBrdGVybWlvczsNCj4gKwlzdHJ1Y3QgdHR5X3N0cnVjdCAqdHR5ID0gaHUtPnR0eTsNCg0K
QXMgYSBnZW5lcmFsIHJ1bGUgdGhlIHZhcmlhYmxlcyB0aGF0IGNhcnJ5IGFuIGFzc2lnbm1lbnQg
ZnJvbSBzb21lIG90aGVyIHN0cnVjdCBjb21lIGZpcnN0Lg0KDQpJRjogQ2hhbmdlZC4NCg0KPiAr
CWludCBzdGF0dXMsIHNwZWVkOw0KPiArDQo+ICsJLyogQnJpbmcgVUFSVCBpbnRvIGRlZmF1bHQg
c2V0dGluZyBhdCAxMTUyMDAgKi8NCg0KSSBkb27igJl0IHRoaW5rIHRoaXMgY29tbWVudCBiZWxv
bmcgaGVyZSBiZWZvcmUgZmx1c2hpbmcgdGhlIGJ1ZmZlci4gSSB0aGluayBoZXJlIHlvdSB3YW50
IGEgY29tbWVudCB0aGF0IGV4cGxhaW4gdGhhdCB3ZSBhcmUgZmx1c2hpbmcgdGhlIGxpbmUgZGlz
Y2lwbGluZSBidWZmZXJzIGFuZCB0aGUgVFRZIGJ1ZmZlcnMuDQoNCklGOiBDaGFuZ2VkLg0KDQo+
ICsJaWYgKHR0eS0+bGRpc2MtPm9wcy0+Zmx1c2hfYnVmZmVyKQ0KPiArCQl0dHktPmxkaXNjLT5v
cHMtPmZsdXNoX2J1ZmZlcih0dHkpOw0KPiArCXR0eV9kcml2ZXJfZmx1c2hfYnVmZmVyKHR0eSk7
DQoNCkFuZCB0aGlzIHNob3VsZCBoYXZlIGFuIGV4dHJhIHNwYWNlIGluIGJldHdlZW4gaGVyZSBh
bmQgdGhlbiBhZGQgdGhlIGNvbW1lbnQgdG8gcHV0IHRoZSBVQVJUIGJhY2sgaW50byBkZWZhdWx0
IG1vZGUuIEkgd291bGQgYWN0dWFsbHkgbGlzdCBhbGwgZGVmYXVsdCBvcHRpb25zIHdlIGFyZSBy
ZXF1aXJpbmcgYW5kIG5vdCBqdXN0IHRoZSBzcGVlZC4NCg0KSUY6IERvbmUuDQoNCj4gKwlrdGVy
bWlvcyA9IHR0eS0+dGVybWlvczsNCj4gKwlCVF9EQkcoImluaXRfdWFydCBkZWYgZmxhZ3MgY19v
ICV4IGNfbCAleCBjX2MgJXggc3BkICVkLyVkIiwNCj4gKwkgICAgICAga3Rlcm1pb3MuY19vZmxh
Zywga3Rlcm1pb3MuY19sZmxhZywga3Rlcm1pb3MuY19jZmxhZywNCj4gKwkgICAgICAga3Rlcm1p
b3MuY19pc3BlZWQsIGt0ZXJtaW9zLmNfb3NwZWVkKTsNCj4gKwkgICAgICAga3Rlcm1pb3MuY19p
ZmxhZyAmPSB+KElHTkJSSyB8IEJSS0lOVCB8IFBBUk1SSyB8IElTVFJJUA0KPiArCQl8IElOTENS
IHwgSUdOQ1IgfCBJQ1JOTCB8IElYT04pOw0KPiArCWt0ZXJtaW9zLmNfb2ZsYWcgJj0gfk9QT1NU
Ow0KPiArCWt0ZXJtaW9zLmNfbGZsYWcgJj0gfihFQ0hPIHwgRUNIT05MIHwgSUNBTk9OIHwgSVNJ
RyB8IElFWFRFTik7DQo+ICsJa3Rlcm1pb3MuY19jZmxhZyAmPSB+KENTSVpFIHwgUEFSRU5CIHwg
Q0JBVUQpOw0KPiArCWt0ZXJtaW9zLmNfY2ZsYWcgfD0gQ1M4Ow0KPiArCWt0ZXJtaW9zLmNfY2Zs
YWcgfD0gQ1JUU0NUUzsNCj4gKwlrdGVybWlvcy5jX2NmbGFnIHw9IEIxMTUyMDA7DQo+ICsJa3Rl
cm1pb3MuY19pc3BlZWQgPSAxMTUyMDA7DQo+ICsJa3Rlcm1pb3MuY19vc3BlZWQgPSAxMTUyMDA7
DQo+ICsJc3RhdHVzID0gdHR5X3NldF90ZXJtaW9zKHR0eSwgJmt0ZXJtaW9zKTsNCg0KSW4gZ2Vu
ZXJhbCB0aGUgdmFyaWFibGUgbmFtZSBlcnIgaXMgbW9zdGx5IHVzZWQgZm9yIHRoaXMgYW5kIG5v
dCBzdGF0dXMuDQoNCklGOiBDaGFuZ2VkLg0KDQo+ICsJaWYgKHN0YXR1cykgew0KPiArCQlCVF9E
QkcoImluaXRfdWFydCBzZXRfdGVybWlvcyBmYWlsdXJlICVkIiwgc3RhdHVzKTsNCj4gKwkJcmV0
dXJuIHN0YXR1czsNCj4gKwl9DQo+ICsNCj4gKwlzcGVlZCA9IHR0eV9nZXRfYmF1ZF9yYXRlKHR0
eSk7DQo+ICsJQlRfREJHKCJpbml0X3VhcnQgc2V0X3Rlcm1pb3MgY29tcGxldGVkLCBzcGQgJWQi
LCBzcGVlZCk7DQoNClRoaXMgd2FycmFudHMgYW4gZXh0cmEgZW1wdHkgbGluZSBmb3IgZWFzaWVy
IHJlYWRhYmlsaXR5Lg0KDQpJRjogQWRkZWQuDQoNCj4gKwlrdGVybWlvcyA9IHR0eS0+dGVybWlv
czsNCj4gKwlCVF9EQkcoImluaXRfdWFydCBuZXcgZmxhZ3MgY19vICV4IGNfbCAleCBjX2MgJXgg
c3BkICVkLyVkIiwNCj4gKwkgICAgICAga3Rlcm1pb3MuY19vZmxhZywga3Rlcm1pb3MuY19sZmxh
Zywga3Rlcm1pb3MuY19jZmxhZywNCj4gKwkgICAgICAga3Rlcm1pb3MuY19pc3BlZWQsIGt0ZXJt
aW9zLmNfb3NwZWVkKTsNCg0KSG93ZXZlciBhIGxvdCBvZiB0aGlzIHB1cmUgZGVidWcgY29kZSBk
dXJpbmcgZGV2ZWxvcG1lbnQuIERvIHlvdSB3YW50IHRvIGtlZXAgaXQgYXJvdW5kPw0KDQpJRjog
UmVtb3ZlZCBtb3N0IG9mIGl0Lg0KDQo+ICsNCj4gKwlyZXR1cm4gMDsNCj4gK30NCj4gK0VYUE9S
VF9TWU1CT0xfR1BMKGJ0YmNtX2luaXRfdWFydCk7DQoNCkkgd29uZGVyIGlmIHRoaXMgY29kZSBh
Y3R1YWxseSBiZWxvbmdzIGludG8gYnRiY20gbW9kdWxlLiBJdCBzb3VuZHMgbGlrZSBwcmV0dHkg
Z2VuZXJpYyBjb2RlIHRoYXQgY291bGQgYmUganVzdCBkb25lIGluIGhjaV9sZGlzYy5jIGFuZCBj
b3VsZCBiZSBkb25lIGJlZm9yZWhhbmQuIE1haW5seSBzaW5jZSBhbGwgdmVuZG9ycyBoYXZlIHNv
bWUgc29ydCBvZiBkZWZhdWx0IGJhdWQgcmF0ZSBhbmQgb3BlcmF0aW9uYWwgYmF1ZCByYXRlLiBX
ZSB3YW50IHRvIGxpbWl0IG1lc3Npbmcgd2l0aCB0aGUgVFRZIGNvZGUgdG8gYSBjZW50cmFsIGxv
Y2F0aW9uLiBJIGRvIG5vdCB3YW50IHRvIGR1cGxpY2F0ZSB0aGlzIGNvZGUgZm9yIEludGVsLCBS
ZWFsdGVrIG9yIGFueSBvdGhlciBoYXJkd2FyZSBtYW51ZmFjdHVyZXIuDQoNCkkgdGhpbmsgdGhh
dCBGcmVkIHdhcyBkb2luZyB0aGF0IGluIGEgY2VudHJhbCBsb2NhdGlvbiBpbiBoY2lfbGRpc2Mu
YyB0byBoYXZlIHRoZSBIQ0kgVUFSVCBjb3JlIGhhbmRsaW5nIHByZXBhcmUgdGhpcy4NCg0KSUY6
IEFnYWluLCBwbGVhc2UgYXBwcm92ZSBGcmVkJ3MgcGF0Y2hlcyBhbmQgSSB3aWxsIGdsYWRseSB1
c2UgdGhlbS4NCg0KPiArDQo+ICsvKg0KPiArICogU2V0IHRoZSBiYXVkIHJhdGUgb24gdGhlIFVB
UlQgYW5kIHRoZSBkZXZpY2UNCj4gKyAqLw0KPiAraW50IGJ0YmNtX3NldF9iYXVkX3JhdGUoc3Ry
dWN0IGhjaV91YXJ0ICpodSwgaW50IGJhdWRfcmF0ZSkNCj4gK3sNCg0KQXMgSSBzYWlkIGFib3Zl
LiBUaGlzIGlzIGEgbGF5ZXIgdmlvbGF0aW9uLiBzdHJ1Y3QgaGNpX3VhcnQgaXMgaW50ZXJuYWwg
dG8gdGhlIGhjaV91YXJ0LmtvIGRyaXZlci4gWW91IGNhbiBub3QgZXhwb3NlIHRoaXMgZnJvbSB0
aGUgYnRiY20ua28gZHJpdmVyLg0KDQpJRjogQWdhaW4sIHBsZWFzZSBhcHByb3ZlIEZyZWQncyBw
YXRjaGVzIGFuZCBJIHdpbGwgZ2xhZGx5IHVzZSB0aGVtLg0KDQo+ICsJc3RydWN0IGt0ZXJtaW9z
IGt0ZXJtaW9zOw0KPiArCXN0cnVjdCB0dHlfc3RydWN0ICp0dHkgPSBodS0+dHR5Ow0KPiArCWlu
dCBzdGF0dXMsIHNwZWVkLCBjZmxhZzsNCj4gKwlzdHJ1Y3Qgc2tfYnVmZiAqc2tiOw0KPiArCXVu
c2lnbmVkIGNoYXIgZW5hYmxlID0gMTsNCj4gKwl1bnNpZ25lZCBjaGFyIGJhdWRfcmF0ZV92c2Nf
cGFyc1tdID0gezAsIDAsIDAsIDB4MTAsIDB4MGUsIDB9Ow0KDQpNaWdodCB3YW50IHRvIHVzZSB1
OCBoZXJlIGluc3RlYWQuIEFuZCAweDAxIGluc3RlYWQgb2YgMSBmb3IgdGhlc2UgYXNzaWdubWVu
dHMuDQoNCklGOiBDaGFuZ2VkLg0KDQo+ICsNCj4gKwkvKiBJZiB0aGUgYmF1ZCByYXRlIGlzIGhp
Z2hlciB0aGFuIDMwMDAwMDAsIGNoYW5nZSB0aGUgY2xvY2sgKi8NCj4gKwlpZiAoYmF1ZF9yYXRl
ID4gMzAwMDAwMCkgew0KPiArCQlza2IgPSBfX2hjaV9jbWRfc3luYyhodS0+aGRldiwgMHhmYzQ1
LCAxLCAmZW5hYmxlLA0KPiArCQkJCSAgICAgSENJX0lOSVRfVElNRU9VVCk7DQo+ICsJCWlmIChJ
U19FUlIoc2tiKSkgew0KPiArCQkJc3RhdHVzID0gUFRSX0VSUihza2IpOw0KPiArCQkJcmV0dXJu
IHN0YXR1czsNCj4gKwkJfQ0KPiArDQo+ICsJCWtmcmVlX3NrYihza2IpOw0KPiArCQlCVF9EQkco
InNldF9iYXVkX3JhdGUgd3JpdGUgVUFSVCA0OCBNSHogVlNDIHN1Y2NlZWRlZCIpOw0KPiArCX0N
Cg0KSSB3b25kZXIgaWYgY2FuIGFsd2F5cyBhc3N1bWUgdGhhdCB0aGUgY2xvY2sgaXMgc2V0IGNv
cnJlY3RseSBhbmQgbm90IGJldHRlciBzd2l0Y2ggdGhlIGNsb2NrIGJhY2sgaW4gY2FzZSB0aGUg
dmFsdWUgaXMgbG93ZXIuDQoNCkFsc28gaXMgdGhpcyBub3cgcmVhbGx5IGVuYWJsZSBvciBqdXN0
IGEgY2xvY2sgbW9kZSBzZWxlY3Rpb24/DQoNCklGOiBUaGlzIGlzIGNsb2NrIG1vZGUgc2VsZWN0
aW9uIGZvciBoaWdoZXIgYmF1ZCByYXRlcy4gVGhlIGRlZmF1bHQgY2xvY2sgaXMgZ29vZCBmb3Ig
bG93ZXIgcmF0ZXMuDQoNCj4gKw0KPiArCS8qIE5vdyBsZXQgdGhlIGRldmljZSBrbm93IGFib3V0
IHRoZSByYXRlIGNoYW5nZSAqLw0KPiArCWJhdWRfcmF0ZV92c2NfcGFyc1syXSA9ICh1bnNpZ25l
ZCBjaGFyKShiYXVkX3JhdGUgJiAweGZmKTsNCj4gKwliYXVkX3JhdGVfdnNjX3BhcnNbM10gPSAo
dW5zaWduZWQgY2hhcikoKGJhdWRfcmF0ZSA+PiA4KSAmIDB4ZmYpOw0KPiArCWJhdWRfcmF0ZV92
c2NfcGFyc1s0XSA9ICh1bnNpZ25lZCBjaGFyKSgoYmF1ZF9yYXRlID4+IDE2KSAmIDB4ZmYpOw0K
PiArCWJhdWRfcmF0ZV92c2NfcGFyc1s1XSA9ICh1bnNpZ25lZCBjaGFyKSgoYmF1ZF9yYXRlID4+
IDI0KSAmIDB4ZmYpOw0KDQpXZSBoYXZlIGZ1bmN0aW9uIGxpa2UgcHV0X3VuYWxpZ25lZF9sZTMy
IGZvciB0aGlzLiBPciBqdXN0IGRlZmluZSB0aGUgdmVuZG9yIGNvbW1hbmQgc3RydWN0IGZvciB0
aGlzLg0KDQpJRjogQ2hhbmdlZC4NCg0KPiArCXNrYiA9IF9faGNpX2NtZF9zeW5jKGh1LT5oZGV2
LCAweGZjMTgsIHNpemVvZihiYXVkX3JhdGVfdnNjX3BhcnMpLA0KPiArCQkJICAgICBiYXVkX3Jh
dGVfdnNjX3BhcnMsIEhDSV9JTklUX1RJTUVPVVQpOw0KPiArCWlmIChJU19FUlIoc2tiKSkgew0K
PiArCQlzdGF0dXMgPSBQVFJfRVJSKHNrYik7DQo+ICsJCUJUX0VSUigic2V0X2JhdWRfcmF0ZSBW
U0MgZmFpbGVkICglZCkiLCBzdGF0dXMpOw0KPiArCQlyZXR1cm4gc3RhdHVzOw0KPiArCQl9DQoN
ClRoZSB9IGlzIGF0IHRoZSB3cm9uZyBpbmRlbnRhdGlvbi4NCg0KSUY6IEZpeGVkLg0KDQo+ICsN
Cj4gKwlrZnJlZV9za2Ioc2tiKTsNCj4gKwlCVF9EQkcoInNldF9iYXVkX3JhdGUgVlNDIHN1Y2Nl
ZWRlZCIpOw0KPiArDQo+ICsJLyogU2V0IFVBUlQgaW50byB0aGlzIHJhdGUgYXMgd2VsbCAqLw0K
PiArCWt0ZXJtaW9zID0gdHR5LT50ZXJtaW9zOw0KPiArCUJUX0RCRygic2V0X2JhdWRfcmF0ZSBz
dGFydCBmbGFncyBjX28gJXggY19sICV4IGNfYyAleCBzcGQgJWQvJWQiLA0KPiArCSAgICAgICBr
dGVybWlvcy5jX29mbGFnLCBrdGVybWlvcy5jX2xmbGFnLCBrdGVybWlvcy5jX2NmbGFnLA0KPiAr
CSAgICAgICBrdGVybWlvcy5jX2lzcGVlZCwga3Rlcm1pb3MuY19vc3BlZWQpOw0KPiArCXN3aXRj
aCAoYmF1ZF9yYXRlKSB7DQo+ICsJY2FzZSAxMTUyMDA6DQo+ICsJCWNmbGFnIHw9IEIxMTUyMDA7
IGJyZWFrOw0KPiArCWNhc2UgOTIxNjAwOg0KPiArCQljZmxhZyB8PSBCOTIxNjAwOyBicmVhazsN
Cj4gKwljYXNlIDMwMDAwMDA6DQo+ICsJCWNmbGFnIHw9IEIzMDAwMDAwOyBicmVhazsNCj4gKwlj
YXNlIDM1MDAwMDA6DQo+ICsJCWNmbGFnIHw9IEIzNTAwMDAwOyBicmVhazsNCj4gKwljYXNlIDQw
MDAwMDA6DQo+ICsJCWNmbGFnIHw9IEI0MDAwMDAwOyBicmVhazsNCj4gKwlkZWZhdWx0Og0KPiAr
CQlCVF9EQkcoInNldF9iYXVkX3JhdGUgdW5rbm93biByYXRlICVkIiwgYmF1ZF9yYXRlKTsNCj4g
KwkJcmV0dXJuIC1FSU5WQUw7DQo+ICsJfQ0KPiArDQo+ICsJa3Rlcm1pb3MuY19jZmxhZyAmPSB+
Q0JBVUQ7DQo+ICsJa3Rlcm1pb3MuY19jZmxhZyB8PSBjZmxhZzsNCj4gKwlrdGVybWlvcy5jX2lz
cGVlZCA9IGJhdWRfcmF0ZTsNCj4gKwlrdGVybWlvcy5jX29zcGVlZCA9IGJhdWRfcmF0ZTsNCj4g
KwlzdGF0dXMgPSB0dHlfc2V0X3Rlcm1pb3ModHR5LCAma3Rlcm1pb3MpOw0KPiArCWlmIChzdGF0
dXMpIHsNCj4gKwkJQlRfREJHKCJzZXRfYmF1ZF9yYXRlIHNldF90ZXJtaW9zIGZhaWx1cmUgJWQi
LCBzdGF0dXMpOw0KPiArCQlyZXR1cm4gc3RhdHVzOw0KPiArCX0NCj4gKw0KPiArCXNwZWVkID0g
dHR5X2dldF9iYXVkX3JhdGUodHR5KTsNCj4gKwlCVF9EQkcoInNldF9iYXVkX3JhdGUgc2V0X3Rl
cm1pb3MgY29tcGxldGVkLCBzcGQgJWQiLCBzcGVlZCk7DQo+ICsJa3Rlcm1pb3MgPSB0dHktPnRl
cm1pb3M7DQo+ICsJQlRfREJHKCJzZXRfYmF1ZF9yYXRlIGZsYWdzIGNfbyAleCBjX2wgJXggY19j
ICV4IHNwZCAlZC8lZCIsDQo+ICsJICAgICAgIGt0ZXJtaW9zLmNfb2ZsYWcsIGt0ZXJtaW9zLmNf
bGZsYWcsIGt0ZXJtaW9zLmNfY2ZsYWcsDQo+ICsJICAgICAgIGt0ZXJtaW9zLmNfaXNwZWVkLCBr
dGVybWlvcy5jX29zcGVlZCk7DQoNCkR1cGxpY2F0aW5nIHRoZSBhY3R1YWwgVFRZIGJhdWQgcmF0
ZSBjaGFuZ2UgY29kZSBoZXJlIG1ha2VzIGl0IGNsZWFyIHRoYXQgd2Ugd2FudCB0aGF0IGNlbnRy
YWxpc2VkIGluIGhjaV9sZGlzYy4NCg0KSUY6IExvb2tpbmcgZm9yd2FyZCB0byB1c2luZyBGcmVk
J3MgY2hhbmdlcy4NCg0KPiArDQo+ICsJcmV0dXJuIDA7DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9M
X0dQTChidGJjbV9zZXRfYmF1ZF9yYXRlKTsNCj4gKw0KPiBpbnQgYnRiY21fc2V0dXBfcGF0Y2hy
YW0oc3RydWN0IGhjaV9kZXYgKmhkZXYpDQo+IHsNCj4gCWNoYXIgZndfbmFtZVs2NF07DQo+IEBA
IC0yNzUsNyArNDA3LDggQEAgaW50IGJ0YmNtX3NldHVwX3BhdGNocmFtKHN0cnVjdCBoY2lfZGV2
ICpoZGV2KQ0KPiAJY29uc3QgY2hhciAqaHdfbmFtZSA9IE5VTEw7DQo+IAlzdHJ1Y3Qgc2tfYnVm
ZiAqc2tiOw0KPiAJc3RydWN0IGhjaV9ycF9yZWFkX2xvY2FsX3ZlcnNpb24gKnZlcjsNCj4gLQlp
bnQgaSwgZXJyOw0KPiArCWludCBpLCBlcnIsIGlzX3VhcnQgPSBmYWxzZTsNCj4gKwlzdHJ1Y3Qg
aGNpX3VhcnQgKmh1ID0gaGNpX2dldF9kcnZkYXRhKGhkZXYpOw0KPiANCj4gCS8qIFJlc2V0ICov
DQo+IAllcnIgPSBidGJjbV9yZXNldChoZGV2KTsNCj4gQEAgLTI5NywxNCArNDMwLDE4IEBAIGlu
dCBidGJjbV9zZXR1cF9wYXRjaHJhbShzdHJ1Y3QgaGNpX2RldiAqaGRldikNCj4gCWlmIChJU19F
UlIoc2tiKSkNCj4gCQlyZXR1cm4gUFRSX0VSUihza2IpOw0KPiANCj4gLQlCVF9JTkZPKCIlczog
QkNNOiBjaGlwIGlkICV1IiwgaGRldi0+bmFtZSwgc2tiLT5kYXRhWzFdKTsNCj4gKwlCVF9JTkZP
KCIlczogQkNNOiBjaGlwIGlkICV1LCByZXYgMHgleCBzdWJ2ZXIgMHgleCIsDQo+ICsJCWhkZXYt
Pm5hbWUsIHNrYi0+ZGF0YVsxXSwgcmV2LCBzdWJ2ZXIpOw0KPiAJa2ZyZWVfc2tiKHNrYik7DQo+
IA0KPiAJc3dpdGNoICgocmV2ICYgMHhmMDAwKSA+PiAxMikgew0KPiAJY2FzZSAwOg0KPiArCWNh
c2UgMToNCj4gCQlmb3IgKGkgPSAwOyBiY21fdWFydF9zdWJ2ZXJfdGFibGVbaV0ubmFtZTsgaSsr
KSB7DQo+IAkJCWlmIChzdWJ2ZXIgPT0gYmNtX3VhcnRfc3VidmVyX3RhYmxlW2ldLnN1YnZlcikg
ew0KPiAJCQkJaHdfbmFtZSA9IGJjbV91YXJ0X3N1YnZlcl90YWJsZVtpXS5uYW1lOw0KPiArCQkJ
CUJUX0lORk8oIlVBUlQgZmlybXdhcmUgZm91bmQ6ICVzIiwgaHdfbmFtZSk7DQo+ICsJCQkJaXNf
dWFydCA9IHRydWU7DQo+IAkJCQlicmVhazsNCj4gCQkJfQ0KPiAJCX0NCj4gQEAgLTMxMiw3ICs0
NDksNyBAQCBpbnQgYnRiY21fc2V0dXBfcGF0Y2hyYW0oc3RydWN0IGhjaV9kZXYgKmhkZXYpDQo+
IAkJc25wcmludGYoZndfbmFtZSwgc2l6ZW9mKGZ3X25hbWUpLCAiYnJjbS8lcy5oY2QiLA0KPiAJ
CQkgaHdfbmFtZSA/IDogIkJDTSIpOw0KPiAJCWJyZWFrOw0KPiAtCWNhc2UgMToNCj4gKw0KPiAJ
Y2FzZSAyOg0KPiAJCS8qIFJlYWQgVVNCIFByb2R1Y3QgSW5mbyAqLw0KPiAJCXNrYiA9IGJ0YmNt
X3JlYWRfdXNiX3Byb2R1Y3QoaGRldik7DQo+IEBAIC0zNDUsMTEgKzQ4MiwyNSBAQCBpbnQgYnRi
Y21fc2V0dXBfcGF0Y2hyYW0oc3RydWN0IGhjaV9kZXYgKmhkZXYpDQo+IAlpZiAoZXJyID09IC1F
Tk9FTlQpDQo+IAkJcmV0dXJuIDA7DQo+IA0KPiArCS8qIE9uY2UgdGhlIHBhdGNoIGlzIGRvd25s
b2FkZWQsIHRoZSBkZXZpY2UgaXMgYmFjayBhdCBkZWZhdWx0IHJhdGUgKi8NCj4gKwlpZiAoaXNf
dWFydCkgew0KPiArCQllcnIgPSBidGJjbV9pbml0X3VhcnQoaHUpOw0KPiArCQlpZiAoZXJyKQ0K
PiArCQkJcmV0dXJuIDA7DQo+ICsJfQ0KPiArDQo+IAkvKiBSZXNldCAqLw0KPiAJZXJyID0gYnRi
Y21fcmVzZXQoaGRldik7DQo+IAlpZiAoZXJyKQ0KPiAJCXJldHVybiBlcnI7DQo+IA0KPiArCWlm
IChpc191YXJ0KSB7DQo+ICsJCWVyciA9IGJ0YmNtX3NldF9iYXVkX3JhdGUoaHUsDQo+ICsJCQkJ
CSAgYmNtX3VhcnRfc3VidmVyX3RhYmxlW2ldLmJhdWRfcmF0ZSk7DQo+ICsJCWlmIChlcnIpDQo+
ICsJCQlyZXR1cm4gMDsNCj4gKwl9DQo+ICsNCg0KVGhlIGlzX3VhcnQgZGVjaXNpb24gaXMgc29t
ZXRoaW5nIHRoYXQgdGhlIGRyaXZlciBzaG91bGQgbWFrZS4gU28gd2hpbGUgcmlnaHQgbm93IGl0
IGlzIGVhc3kgdG8ganVzdCByZWZlcmVuY2UgdGhlIHNldHVwIGZ1bmN0aW9uIGZyb20gdGhlIGNv
bW1vbiBjb2RlLCBJIHRoaW5rIHRoZSByZWFsaXR5IGlzIHRoYXQgZWFjaCBkcml2ZXIgKFVTQiBh
bmQgVUFSVCkgaGFzIHRvIHByb3ZpZGUgaXRzIG93biAtPnNldHVwIGZ1bmN0aW9uIGZvciBCcm9h
ZGNvbSBkZXZpY2VzIGFuZCB0aGVuIHN0aWNrIHRoZSBuZWVkZWQgcGllY2VzIHRvZ2V0aGVyLiBT
byBsZXRzIHRyeSB0byBmaWd1cmUgb3V0IHdoYXQgbmVlZHMgdG8gYmUgZG9uZSB3aGVuIGFuZCB3
aGF0IGNhbiBiZSBnZW5lcmljIGluIGhjaV9sZGlzYy5jIGFuZCB0aGVuIHdlIG1vdmUgcGllY2Vz
IGludG8gdGhlIHJpZ2h0IGxvY2F0aW9uLg0KDQpJRjogQWdyZWUuIEludGVsIGlzIGV4dGVuc2l2
ZWx5IGNoYW5naW5nIHRoaXMgY29kZS4gTGV0J3MgYXBwcm92ZSB3aGF0IEludGVsIGhhcyBkb25l
IHRvIHRoaXMgZXh0ZW50IGFuZCBnbyBmcm9tIHRoZXJlLg0KDQo+IAkvKiBSZWFkIExvY2FsIFZl
cnNpb24gSW5mbyAqLw0KPiAJc2tiID0gYnRiY21fcmVhZF9sb2NhbF92ZXJzaW9uKGhkZXYpOw0K
PiAJaWYgKElTX0VSUihza2IpKQ0KPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ibHVldG9vdGgvYnRi
Y20uaCBiL2RyaXZlcnMvYmx1ZXRvb3RoL2J0YmNtLmgNCj4gaW5kZXggZWI2YWI1Zi4uZjdkMzBj
NiAxMDA2NDQNCj4gLS0tIGEvZHJpdmVycy9ibHVldG9vdGgvYnRiY20uaA0KPiArKysgYi9kcml2
ZXJzL2JsdWV0b290aC9idGJjbS5oDQo+IEBAIC0yLDcgKzIsOCBAQA0KPiAgKg0KPiAgKiAgQmx1
ZXRvb3RoIHN1cHBvcnQgZm9yIEJyb2FkY29tIGRldmljZXMNCj4gICoNCj4gLSAqICBDb3B5cmln
aHQgKEMpIDIwMTUgIEludGVsIENvcnBvcmF0aW9uDQo+ICsgKiAgQ29weXJpZ2h0IChDKSAyMDE1
IEludGVsIENvcnBvcmF0aW9uDQo+ICsgKiAgQ29weXJpZ2h0IChDKSAyMDE1IEJyb2FkY29tIENv
cnBvcmF0aW9uDQo+ICAqDQo+ICAqDQo+ICAqICBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2Fy
ZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQ0KPiBAQCAtMTUsMTAgKzE2
LDYgQEANCj4gICogIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIg
UFVSUE9TRS4gIFNlZSB0aGUNCj4gICogIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBt
b3JlIGRldGFpbHMuDQo+ICAqDQo+IC0gKiAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29w
eSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCj4gLSAqICBhbG9uZyB3aXRoIHRo
aXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQ0KPiAtICogIEZv
dW5kYXRpb24sIEluYy4sIDU5IFRlbXBsZSBQbGFjZSwgU3VpdGUgMzMwLCBCb3N0b24sIE1BICAw
MjExMS0xMzA3ICBVU0ENCj4gLSAqDQo+ICAqLw0KPiANCj4gI2lmIElTX0VOQUJMRUQoQ09ORklH
X0JUX0JDTSkNCj4gQEAgLTMwLDYgKzI3LDggQEAgaW50IGJ0YmNtX3BhdGNocmFtKHN0cnVjdCBo
Y2lfZGV2ICpoZGV2LCBjb25zdCBjaGFyICpmaXJtd2FyZSk7DQo+IGludCBidGJjbV9zZXR1cF9w
YXRjaHJhbShzdHJ1Y3QgaGNpX2RldiAqaGRldik7DQo+IGludCBidGJjbV9zZXR1cF9hcHBsZShz
dHJ1Y3QgaGNpX2RldiAqaGRldik7DQo+IA0KPiAraW50IGJ0YmNtX2luaXRfdWFydChzdHJ1Y3Qg
aGNpX3VhcnQgKmh1KTsNCj4gK2ludCBidGJjbV9zZXRfYmF1ZF9yYXRlKHN0cnVjdCBoY2lfdWFy
dCAqaHUsIGludCBiYXVkX3JhdGUpOw0KPiAjZWxzZQ0KPiANCj4gc3RhdGljIGlubGluZSBpbnQg
YnRiY21fY2hlY2tfYmRhZGRyKHN0cnVjdCBoY2lfZGV2ICpoZGV2KQ0KPiBAQCAtNTcsNCArNTYs
MTQgQEAgc3RhdGljIGlubGluZSBpbnQgYnRiY21fc2V0dXBfYXBwbGUoc3RydWN0IGhjaV9kZXYg
KmhkZXYpDQo+IAlyZXR1cm4gMDsNCj4gfQ0KPiANCj4gK3N0YXRpYyBpbnQgYnRiY21faW5pdF91
YXJ0KHZvaWQgKmh1KQ0KDQpUaGVzZSBzaG91bGQgYmUgc3RhdGljIGlubGluZS4gTm90IHRoYXQg
d2UgY2FuIGtlZXAgdGhlbSB0aGlzIHdheSwgYnV0IGp1c3QgZm9yIGZ1dHVyZSByZWZlcmVuY2Uu
DQoNCklGOiBDaGFuZ2VkLg0KDQo+ICt7DQo+ICsJcmV0dXJuIDA7DQo+ICt9DQo+ICsNCj4gK3N0
YXRpYyBpbnQgYnRiY21fc2V0X2JhdWRfcmF0ZSh2b2lkICpodSwgaW50IGJhdWRfcmF0ZSk7DQo+
ICt7DQo+ICsJcmV0dXJuIDA7DQo+ICt9DQo+ICsNCj4gI2VuZGlmDQo+IGRpZmYgLS1naXQgYS9k
cml2ZXJzL2JsdWV0b290aC9idGJjbV91YXJ0LmMgYi9kcml2ZXJzL2JsdWV0b290aC9idGJjbV91
YXJ0LmMNCj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQNCj4gaW5kZXggMDAwMDAwMC4uMzMwOGJkYg0K
PiAtLS0gL2Rldi9udWxsDQo+ICsrKyBiL2RyaXZlcnMvYmx1ZXRvb3RoL2J0YmNtX3VhcnQuYw0K
PiBAQCAtMCwwICsxLDY3OSBAQA0KPiArLyoNCj4gKyAqDQo+ICsgKiAgQmx1ZXRvb3RoIEJDTSBV
QVJUIERyaXZlcg0KPiArICoNCj4gKyAqICBDb3B5cmlnaHQgKGMpIDIwMTUgQnJvYWRjb20gQ29y
cG9yYXRpb24NCj4gKyAqDQo+ICsgKiAgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlv
dSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkNCj4gKyAqICBpdCB1bmRlciB0aGUg
dGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieQ0K
PiArICogIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9uIDIgb2Yg
dGhlIExpY2Vuc2UsIG9yDQo+ICsgKiAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lv
bi4NCj4gKyAqDQo+ICsgKiAgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3Bl
IHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsDQo+ICsgKiAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZ
OyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YNCj4gKyAqICBNRVJDSEFOVEFC
SUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlDQo+ICsg
KiAgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4NCj4gKyAqDQo+
ICsgKi8NCj4gKw0KPiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPg0KPiArDQo+ICsjaW5jbHVk
ZSA8bGludXgva2VybmVsLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvaW5pdC5oPg0KPiArI2luY2x1
ZGUgPGxpbnV4L3R5cGVzLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvZmNudGwuaD4NCj4gKyNpbmNs
dWRlIDxsaW51eC9pbnRlcnJ1cHQuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9wdHJhY2UuaD4NCj4g
KyNpbmNsdWRlIDxsaW51eC9wb2xsLmg+DQo+ICsNCj4gKyNpbmNsdWRlIDxsaW51eC9zbGFiLmg+
DQo+ICsjaW5jbHVkZSA8bGludXgvdHR5Lmg+DQo+ICsjaW5jbHVkZSA8bGludXgvZXJybm8uaD4N
Cj4gKyNpbmNsdWRlIDxsaW51eC9zdHJpbmcuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9zaWduYWwu
aD4NCj4gKyNpbmNsdWRlIDxsaW51eC9pb2N0bC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L3NrYnVm
Zi5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2xpc3QuaD4NCj4gKw0KPiArI2luY2x1ZGUgPG5ldC9i
bHVldG9vdGgvYmx1ZXRvb3RoLmg+DQo+ICsjaW5jbHVkZSA8bmV0L2JsdWV0b290aC9oY2lfY29y
ZS5oPg0KPiArDQo+ICsjaW5jbHVkZSA8bGludXgvZ3Bpby9jb25zdW1lci5oPg0KPiArI2luY2x1
ZGUgPGxpbnV4L29mLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvb2ZfZ3Bpby5oPg0KPiArI2luY2x1
ZGUgPGxpbnV4L29mX3BsYXRmb3JtLmg+DQo+ICsNCj4gKyNpbmNsdWRlICJidGJjbV91YXJ0Lmgi
DQo+ICsNCj4gK3N0YXRpYyBpbnQgaWRsZVRpbWVvdXQgPSA1Ow0KDQpXZSBkbyBub3QgbmFtZSB2
YXJpYWJsZXMgaW4gY2FtZWwgY2FzZS4gVGhpcyB3b3VsZCBiZSBpZGxlX3RpbWVvdXQuDQoNCklG
OiBDaGFuZ2VkLg0KDQo+ICttb2R1bGVfcGFyYW0oaWRsZVRpbWVvdXQsIGludCwgMCk7DQo+ICtN
T0RVTEVfUEFSTV9ERVNDKGlkbGVUaW1lb3V0LCAiQmx1ZXRvb3RoIGlkbGUgdGltZW91dCBpbiBz
ZWNvbmRzIik7DQo+ICsNCj4gKy8qIERldmljZSBjb250ZXh0ICovDQo+ICtzdHJ1Y3QgYmNtX2Rl
dmljZSB7DQo+ICsJc3RydWN0IGxpc3RfaGVhZCBsaXN0Ow0KPiArDQo+ICsJc3RydWN0IHBsYXRm
b3JtX2RldmljZSAqcGRldjsNCj4gKwlzdHJ1Y3QgZ3Bpb19kZXNjICpidF93YWtlX2dwaW87DQo+
ICsJc3RydWN0IGdwaW9fZGVzYyAqZGV2X3dha2VfZ3BpbzsNCj4gKwlzdHJ1Y3QgZ3Bpb19kZXNj
ICpyZWdfb25fZ3BpbzsNCj4gKwlpbnQgYnRfd2FrZV9pcnE7DQo+ICsJaW50IGRldl93YWtlX2Fj
dGl2ZV9sb3c7DQo+ICsJaW50IHJlZ19vbl9hY3RpdmVfbG93Ow0KPiArCWludCBidF93YWtlX2Fj
dGl2ZV9sb3c7DQo+ICsJdTMyIGNvbmZpZ3VyZV9zbGVlcDsNCj4gKwl1MzIgbWFudWFsX2ZjOw0K
PiArCXUzMiBiYXVkX3JhdGVfYmVmb3JlX2NvbmZpZ19kb3dubG9hZDsNCj4gKwl1MzIgY29uZmln
dXJlX2F1ZGlvOw0KPiArCXUzMiBQQ01DbG9ja01vZGU7DQo+ICsJdTMyIFBDTUZpbGxNZXRob2Q7
DQo+ICsJdTMyIFBDTUZpbGxOdW07DQo+ICsJdTMyIFBDTUZpbGxWYWx1ZTsNCj4gKwl1MzIgUENN
SW5DYWxsQml0Y2xvY2s7DQo+ICsJdTMyIFBDTUxTQkZpcnN0Ow0KPiArCXUzMiBQQ01SaWdodEp1
c3RpZnk7DQo+ICsJdTMyIFBDTVJvdXRpbmc7DQo+ICsJdTMyIFBDTVNob3J0RnJhbWVTeW5jOw0K
PiArCXUzMiBQQ01TeW5jTW9kZTsNCj4gKw0KPiArCWNoYXIgdHR5X25hbWVbNjRdOw0KPiArDQo+
ICsJc3RydWN0IGJ0YmNtX3VhcnRfY2FsbGJhY2tzIHByb3RvY29sX2NhbGxiYWNrczsNCj4gKwlz
dHJ1Y3Qgd29ya19zdHJ1Y3Qgd2FrZXVwX3dvcms7DQo+ICt9Ow0KPiArDQo+ICsvKiBMaXN0IG9m
IEJDTSBCVCBVQVJUIGRldmljZXMgKi8NCj4gK3N0YXRpYyBERUZJTkVfU1BJTkxPQ0soZGV2aWNl
X2xpc3RfbG9jayk7DQo+ICtzdGF0aWMgTElTVF9IRUFEKGRldmljZV9saXN0KTsNCj4gKw0KPiAr
LyoNCj4gKyAqIENhbGxpbmcgdGhlIEJDTSBwcm90b2NvbCBhdCBsb3dlciBleGVjdXRpb24gcHJp
b3JpdHkNCj4gKyAqLw0KPiArc3RhdGljIHZvaWQgYmNtX2J0X3dha2V1cF90YXNrKHN0cnVjdCB3
b3JrX3N0cnVjdCAqd3MpDQo+ICt7DQo+ICsJaW50IHJlc3VtZV9mbGFnOw0KPiArCXN0cnVjdCBi
Y21fZGV2aWNlICpwX2JjbV9kZXZpY2UgPQ0KPiArCQljb250YWluZXJfb2Yod3MsIHN0cnVjdCBi
Y21fZGV2aWNlLCB3YWtldXBfd29yayk7DQo+ICsNCj4gKwlpZiAoIXBfYmNtX2RldmljZSkgew0K
PiArCQlCVF9EQkcoImJjbV9idF93YWtldXBfdGFzayAtIGZhaWxpbmcsIG5vIGRldmljZSIpOw0K
PiArCQlyZXR1cm47DQo+ICsJfQ0KPiArDQo+ICsJLyogTWFrZSBzdXJlIHRoZSBkZXZpY2UgaXMg
cmVzdW1lZCAqLw0KPiArCXJlc3VtZV9mbGFnID0gIXBfYmNtX2RldmljZS0+ZGV2X3dha2VfYWN0
aXZlX2xvdzsNCj4gKwlpZiAocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvKSB7DQo+ICsJCWdw
aW9kX3NldF92YWx1ZShwX2JjbV9kZXZpY2UtPmRldl93YWtlX2dwaW8sIHJlc3VtZV9mbGFnKTsN
Cj4gKwkJQlRfREJHKCJiY21fYnRfd2FrZXVwX3Rhc2sgLSByZXN1bWUgJWQgd3JpdHRlbiwgZGVs
YXlpbmcgMTUgbXMiLA0KPiArCQkgICAgICAgcmVzdW1lX2ZsYWcpOw0KPiArCQltZGVsYXkoMTUp
Ow0KPiArCX0NCj4gKw0KPiArCS8qIExldCB0aGUgcHJvdG9jb2wga25vdyBpdCdzIHRpbWUgdG8g
d2FrZSB1cCAqLw0KPiArCWlmIChwX2JjbV9kZXZpY2UtPnByb3RvY29sX2NhbGxiYWNrcy5wX3dh
a2V1cCkNCj4gKwkJcF9iY21fZGV2aWNlLT5wcm90b2NvbF9jYWxsYmFja3MucF93YWtldXAoDQo+
ICsJCQlwX2JjbV9kZXZpY2UtPnByb3RvY29sX2NhbGxiYWNrcy5jb250ZXh0KTsNCj4gK30NCj4g
Kw0KPiArLyoNCj4gKyAqIEludGVycnVwdCByb3V0aW5lIGZvciB0aGUgd2FrZSBmcm9tIHRoZSBk
ZXZpY2UNCj4gKyAqLw0KPiArc3RhdGljIGlycXJldHVybl90IGJjbV9idF91YXJ0X2lzcihpbnQg
aXJxLCB2b2lkICpjb250ZXh0KQ0KPiArew0KPiArCXVuc2lnbmVkIGludCBidF93YWtlOw0KPiAr
CXN0cnVjdCBiY21fZGV2aWNlICpwID0gKHN0cnVjdCBiY21fZGV2aWNlICopY29udGV4dDsNCj4g
Kw0KPiArCWJ0X3dha2UgPSBncGlvZF9nZXRfdmFsdWUocC0+YnRfd2FrZV9ncGlvKTsNCj4gKwlC
VF9EQkcoImJjbV9idF91YXJ0X2lzciB3aXRoIGJ0X3dha2Ugb2YgJWQgKGFjdGl2ZV9sb3cgJWQp
LCByZXEgYmgiLA0KPiArCSAgICAgICBidF93YWtlLCBwLT5idF93YWtlX2FjdGl2ZV9sb3cpOw0K
PiArDQo+ICsJLyogRGVmZXIgdGhlIGFjdHVhbCBwcm9jZXNzaW5nIHRvIHRoZSBwbGF0Zm9ybSB3
b3JrIHF1ZXVlICovDQo+ICsJc2NoZWR1bGVfd29yaygmcC0+d2FrZXVwX3dvcmspOw0KPiArCXJl
dHVybiBJUlFfSEFORExFRDsNCj4gK30NCj4gKw0KPiArLyoNCj4gKyAqIERldmljZSBpbnN0YW5j
ZSBzdGFydHVwDQo+ICsgKi8NCj4gK3N0YXRpYyBpbnQgYmNtX2J0X3VhcnRfcHJvYmUoc3RydWN0
IHBsYXRmb3JtX2RldmljZSAqcGRldikNCj4gK3sNCj4gKwlpbnQgcmV0ID0gMDsNCj4gKwlzdHJ1
Y3QgZGV2aWNlX25vZGUgKm5wID0gcGRldi0+ZGV2Lm9mX25vZGU7DQo+ICsJY29uc3QgY2hhciAq
dHR5X25hbWU7DQo+ICsJc3RydWN0IGJjbV9kZXZpY2UgKnBfYmNtX2RldmljZSA9IE5VTEw7DQo+
ICsNCj4gKwlwX2JjbV9kZXZpY2UgPSBkZXZtX2t6YWxsb2MoJnBkZXYtPmRldiwgc2l6ZW9mKCpw
X2JjbV9kZXZpY2UpLA0KPiArCQlHRlBfS0VSTkVMKTsNCj4gKwlpZiAoIXBfYmNtX2RldmljZSkg
ew0KPiArCQlCVF9EQkcoImJjbV9idF91YXJ0X3Byb2JlIC0gZmFpbGluZyBkdWUgdG8gbm8gbWVt
b3J5Iik7DQo+ICsJCXJldHVybiAtRU5PTUVNOw0KPiArCX0NCj4gKwlwX2JjbV9kZXZpY2UtPnBk
ZXYgPSBwZGV2Ow0KPiArCUJUX0RCRygiYmNtX2J0X3VhcnRfcHJvYmUgJXAgY29udGV4dCIsIHBf
YmNtX2RldmljZSk7DQo+ICsNCj4gKwkvKiBHZXQgZGV2IHdha2UgR1BJTyAqLw0KPiArCXBfYmNt
X2RldmljZS0+ZGV2X3dha2VfZ3BpbyA9IGdwaW9kX2dldCgmcGRldi0+ZGV2LCAiYnQtd2FrZSIp
Ow0KPiArCUJUX0RCRygiYmNtX2J0X3VhcnRfcHJvYmUgLSBncGlvZF9nZXQgZm9yIGJ0LXdha2Ug
cmV0dXJuZWQgJXAiLA0KPiArCSAgICAgICBwX2JjbV9kZXZpY2UtPmRldl93YWtlX2dwaW8pOw0K
PiArCWlmIChJU19FUlIocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvKSkgew0KPiArCQlyZXQg
PSBQVFJfRVJSKHBfYmNtX2RldmljZS0+ZGV2X3dha2VfZ3Bpbyk7DQo+ICsJCWlmIChyZXQgIT0g
LUVOT0VOVCkgew0KPiArCQkJZGV2X2VycigmcGRldi0+ZGV2LA0KPiArCQkJCSJiY21fYnRfdWFy
dF9wcm9iZSAtIGRldl93YWtlIEdQSU86ICVkXG4iLCByZXQpOw0KPiArCQl9DQo+ICsJCXBfYmNt
X2RldmljZS0+ZGV2X3dha2VfZ3BpbyA9IE5VTEw7DQo+ICsJfSBlbHNlIHsNCj4gKwkJaW50IHJl
c3VtZV9mbGFnOw0KPiArDQo+ICsJCXBfYmNtX2RldmljZS0+ZGV2X3dha2VfYWN0aXZlX2xvdyA9
IGdwaW9kX2lzX2FjdGl2ZV9sb3cNCj4gKwkJCShwX2JjbV9kZXZpY2UtPmRldl93YWtlX2dwaW8p
Ow0KPiArCQlCVF9EQkcoImJjbV9idF91YXJ0X3Byb2JlIC0gZGV2X3dha2UgYS1sb3cgaXMgJWQg
KGNhbnMgJWQpIiwNCj4gKwkJICAgICAgIHBfYmNtX2RldmljZS0+ZGV2X3dha2VfYWN0aXZlX2xv
dywNCj4gKwkJICAgICAgIGdwaW9kX2NhbnNsZWVwKHBfYmNtX2RldmljZS0+ZGV2X3dha2VfZ3Bp
bykpOw0KPiArDQo+ICsJCS8qIGNvbmZpZ3VyZSBkZXZfd2FrZSBhcyBvdXRwdXQgd2l0aCBpbml0
IHJlc3VtZWQgc3RhdGUgKi8NCj4gKwkJcmVzdW1lX2ZsYWcgPSAhcF9iY21fZGV2aWNlLT5kZXZf
d2FrZV9hY3RpdmVfbG93Ow0KPiArCQlyZXQgPSBncGlvZF9kaXJlY3Rpb25fb3V0cHV0KHBfYmNt
X2RldmljZS0+ZGV2X3dha2VfZ3BpbywNCj4gKwkJCQkJICAgICByZXN1bWVfZmxhZyk7DQo+ICsJ
CWlmIChyZXQgPCAwKSB7DQo+ICsJCQlkZXZfZXJyKCZwZGV2LT5kZXYsDQo+ICsJCQkJImJjbV9i
dF91YXJ0X3Byb2JlIHMgZGV2X3dha2UgR1BJTzogJWRcbiIsIHJldCk7DQo+ICsJCQlncGlvZF9w
dXQocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvKTsNCj4gKwkJCXBfYmNtX2RldmljZS0+ZGV2
X3dha2VfZ3BpbyA9IE5VTEw7DQo+ICsJCQlnb3RvIGVuZDsNCj4gKwkJfSBlbHNlIHsNCj4gKwkJ
CUJUX0RCRygiYmNtX2J0X3VhcnRfcHJvYmUgLSBkZXZfd2FrZSBzZXQgdG8gJWQiLA0KPiArCQkJ
ICAgICAgIHJlc3VtZV9mbGFnKTsNCj4gKwkJfQ0KPiArCX0NCj4gKw0KPiArCS8qIEdldCBwb3dl
ciBvbi9vZmYgR1BJTyAqLw0KPiArCXBfYmNtX2RldmljZS0+cmVnX29uX2dwaW8gPSBncGlvZF9n
ZXQoJnBkZXYtPmRldiwgInJlZy1vbiIpOw0KPiArCUJUX0RCRygiYmNtX2J0X3VhcnRfcHJvYmUg
LSBncGlvZF9nZXQgZm9yIHJlZy1vbiByZXR1cm5lZCAlcCIsDQo+ICsJICAgICAgIHBfYmNtX2Rl
dmljZS0+cmVnX29uX2dwaW8pOw0KPiArCWlmIChJU19FUlIocF9iY21fZGV2aWNlLT5yZWdfb25f
Z3BpbykpIHsNCj4gKwkJcmV0ID0gUFRSX0VSUihwX2JjbV9kZXZpY2UtPnJlZ19vbl9ncGlvKTsN
Cj4gKwkJaWYgKHJldCAhPSAtRU5PRU5UKSB7DQo+ICsJCQlkZXZfZXJyKCZwZGV2LT5kZXYsDQo+
ICsJCQkJImJjbV9idF91YXJ0X3Byb2JlIC0gcmVnX29uIEdQSU86ICVkXG4iLCByZXQpOw0KPiAr
CQl9DQo+ICsJCXBfYmNtX2RldmljZS0+cmVnX29uX2dwaW8gPSBOVUxMOw0KPiArCX0gZWxzZSB7
DQo+ICsJCWludCBwb3dlcm9uX2ZsYWc7DQo+ICsNCj4gKwkJcF9iY21fZGV2aWNlLT5yZWdfb25f
YWN0aXZlX2xvdyA9IGdwaW9kX2lzX2FjdGl2ZV9sb3cNCj4gKwkJCShwX2JjbV9kZXZpY2UtPnJl
Z19vbl9ncGlvKTsNCj4gKwkJQlRfREJHKCJiY21fYnRfdWFydF9wcm9iZSAtIHJlZ19vbiBhLWxv
dyBpcyAlZCAoY2FucyAlZCkiLA0KPiArCQkgICAgICAgcF9iY21fZGV2aWNlLT5yZWdfb25fYWN0
aXZlX2xvdywNCj4gKwkJICAgICAgIGdwaW9kX2NhbnNsZWVwKHBfYmNtX2RldmljZS0+cmVnX29u
X2dwaW8pKTsNCj4gKw0KPiArCQkvKiBjb25maWd1cmUgcmVnX29uIGFzIG91dHB1dCB3aXRoIGlu
aXQgb24gc3RhdGUgKi8NCj4gKwkJcG93ZXJvbl9mbGFnID0gIXBfYmNtX2RldmljZS0+cmVnX29u
X2FjdGl2ZV9sb3c7DQo+ICsJCXJldCA9IGdwaW9kX2RpcmVjdGlvbl9vdXRwdXQocF9iY21fZGV2
aWNlLT5yZWdfb25fZ3BpbywNCj4gKwkJCQkJICAgICBwb3dlcm9uX2ZsYWcpOw0KPiArCQlpZiAo
cmV0IDwgMCkgew0KPiArCQkJZGV2X2VycigmcGRldi0+ZGV2LA0KPiArCQkJCSJiY21fYnRfdWFy
dF9wcm9iZSBzIHJlZ19vbiBHUElPOiAlZFxuIiwgcmV0KTsNCj4gKwkJCWdwaW9kX3B1dChwX2Jj
bV9kZXZpY2UtPnJlZ19vbl9ncGlvKTsNCj4gKwkJCXBfYmNtX2RldmljZS0+cmVnX29uX2dwaW8g
PSBOVUxMOw0KPiArCQl9IGVsc2Ugew0KPiArCQkJQlRfREJHKCJiY21fYnRfdWFydF9wcm9iZSAt
IHJlZ19vbiBpbml0aWFsbHkgc2V0IHRvICVkIiwNCj4gKwkJCSAgICAgICBwb3dlcm9uX2ZsYWcp
Ow0KPiArCQl9DQo+ICsJfQ0KPiArDQo+ICsJcGxhdGZvcm1fc2V0X2RydmRhdGEocGRldiwgcF9i
Y21fZGV2aWNlKTsNCj4gKwkvKiBNdXN0IGJlIGRvbmUgYmVmb3JlIGludGVycnVwdCBpcyByZXF1
ZXN0ZWQgKi8NCj4gKwlJTklUX1dPUksoJnBfYmNtX2RldmljZS0+d2FrZXVwX3dvcmssIGJjbV9i
dF93YWtldXBfdGFzayk7DQo+ICsNCj4gKwkvKiBHZXQgYnQgaG9zdCB3YWtlIEdQSU8gKi8NCj4g
KwlwX2JjbV9kZXZpY2UtPmJ0X3dha2VfZ3BpbyA9IGdwaW9kX2dldCgmcGRldi0+ZGV2LCAiYnQt
aG9zdC13YWtlIik7DQo+ICsJQlRfREJHKCJiY21fYnRfdWFydF9wcm9iZSAtIGdwaW9kX2dldCBm
b3IgYnQtaG9zdC13YWtlIHJldHVybmVkICVwIiwNCj4gKwkgICAgICAgcF9iY21fZGV2aWNlLT5i
dF93YWtlX2dwaW8pOw0KPiArCWlmIChJU19FUlIocF9iY21fZGV2aWNlLT5idF93YWtlX2dwaW8p
KSB7DQo+ICsJCXJldCA9IFBUUl9FUlIocF9iY21fZGV2aWNlLT5idF93YWtlX2dwaW8pOw0KPiAr
CQlpZiAocmV0ICE9IC1FTk9FTlQpIHsNCj4gKwkJCWRldl9lcnIoJnBkZXYtPmRldiwNCj4gKwkJ
CQkiYmNtX2J0X3VhcnRfcHJvYmUgLSBidF93YWtlIEdQSU86ICVkXG4iLCByZXQpOw0KPiArCQl9
DQo+ICsJCXBfYmNtX2RldmljZS0+YnRfd2FrZV9ncGlvID0gTlVMTDsNCj4gKwl9IGVsc2Ugew0K
PiArCQkvKiBjb25maWd1cmUgYnRfd2FrZSBhcyBpbnB1dCAqLw0KPiArCQlyZXQgPSBncGlvZF9k
aXJlY3Rpb25faW5wdXQocF9iY21fZGV2aWNlLT5idF93YWtlX2dwaW8pOw0KPiArCQlpZiAocmV0
IDwgMCkgew0KPiArCQkJZGV2X2VycigmcGRldi0+ZGV2LA0KPiArCQkJCSJiY21fYnRfdWFydF9w
cm9iZSBzIGJ0X3dha2UgR1BJTzogJWRcbiIsIHJldCk7DQo+ICsJCQlncGlvZF9wdXQocF9iY21f
ZGV2aWNlLT5idF93YWtlX2dwaW8pOw0KPiArCQkJcF9iY21fZGV2aWNlLT5idF93YWtlX2dwaW8g
PSBOVUxMOw0KPiArCQl9IGVsc2Ugew0KPiArCQkJcF9iY21fZGV2aWNlLT5idF93YWtlX2FjdGl2
ZV9sb3cgPSBncGlvZF9pc19hY3RpdmVfbG93DQo+ICsJCQkJKHBfYmNtX2RldmljZS0+YnRfd2Fr
ZV9ncGlvKTsNCj4gKwkJCUJUX0RCRygiYmNtX2J0X3VhcnRfcHJvYmUgLWJ0X3dha2UgYS1sb3cg
aXMgJWQoY2FucyVkKSIsDQo+ICsJCQkgICAgICAgcF9iY21fZGV2aWNlLT5idF93YWtlX2FjdGl2
ZV9sb3csDQo+ICsJCQkgICAgICAgZ3Bpb2RfY2Fuc2xlZXAocF9iY21fZGV2aWNlLT5idF93YWtl
X2dwaW8pKTsNCj4gKwkJCXBfYmNtX2RldmljZS0+YnRfd2FrZV9pcnEgPSBncGlvZF90b19pcnEN
Cj4gKwkJCQkocF9iY21fZGV2aWNlLT5idF93YWtlX2dwaW8pOw0KPiArCQkJaWYgKHBfYmNtX2Rl
dmljZS0+YnRfd2FrZV9pcnEgPCAwKSB7DQo+ICsJCQkJZGV2X2VycigmcGRldi0+ZGV2LA0KPiAr
CQkJCSJiY21fYnRfdWFydF9wcm9iZSAtIEhPU1RfV0FLRSBJUlE6ICVkXG4iLCByZXQpOw0KPiAr
CQkJfSBlbHNlIHsNCj4gKwkJCQl1bnNpZ25lZCBsb25nIGludGZsYWdzID0gSVJRRl9UUklHR0VS
X1JJU0lORzsNCj4gKw0KPiArCQkJCWlmIChwX2JjbV9kZXZpY2UtPmJ0X3dha2VfYWN0aXZlX2xv
dykNCj4gKwkJCQkJaW50ZmxhZ3MgPSBJUlFGX1RSSUdHRVJfRkFMTElORzsNCj4gKw0KPiArCQkJ
CXJldCA9IHJlcXVlc3RfaXJxKHBfYmNtX2RldmljZS0+YnRfd2FrZV9pcnEsDQo+ICsJCQkJCQkg
IGJjbV9idF91YXJ0X2lzciwNCj4gKwkJCQkJCSAgaW50ZmxhZ3MsICJidF9ob3N0X3dha2UiLA0K
PiArCQkJCQkJICBwX2JjbV9kZXZpY2UpOw0KPiArCQkJCWlmIChyZXQgPCAwKSB7DQo+ICsJCQkJ
CWRldl9lcnIoJnBkZXYtPmRldiwgImJjbV9idF91YXJ0X3Byb2JlIC0gZmFpbGVkIHRvIGNvbmYg
SVJRICVkOiAlZCIsDQo+ICsJCQkJCQlwX2JjbV9kZXZpY2UtPmJ0X3dha2VfaXJxLCByZXQpOw0K
PiArCQkJCX0gZWxzZSB7DQo+ICsJCQkJCUJUX0RCRygiYmNtX2J0X3VhcnRfcHJvYmUgLSBJUlEg
JWQiLA0KPiArCQkJCQkgICAgICAgcF9iY21fZGV2aWNlLT5idF93YWtlX2lycSk7DQo+ICsJCQkJ
fQ0KPiArCQkJfQ0KPiArCQl9DQo+ICsJfQ0KPiArDQo+ICsJcF9iY21fZGV2aWNlLT5jb25maWd1
cmVfc2xlZXAgPSAwOw0KPiArCWlmICghb2ZfcHJvcGVydHlfcmVhZF91MzIobnAsICJjb25maWd1
cmUtc2xlZXAiLA0KPiArCQkJCSAgJnBfYmNtX2RldmljZS0+Y29uZmlndXJlX3NsZWVwKSkgew0K
PiArCQlCVF9EQkcoImNvbmZpZ3VyZS1zbGVlcCByZWFkIGFzICVkIiwNCj4gKwkJCXBfYmNtX2Rl
dmljZS0+Y29uZmlndXJlX3NsZWVwKTsNCj4gKwl9DQo+ICsJcF9iY21fZGV2aWNlLT5tYW51YWxf
ZmMgPSAwOw0KPiArCWlmICghb2ZfcHJvcGVydHlfcmVhZF91MzIobnAsICJtYW51YWwtZmMiLA0K
PiArCQkJCSAgJnBfYmNtX2RldmljZS0+bWFudWFsX2ZjKSkgew0KPiArCQlCVF9EQkcoIm1hbnVh
bC1mYyByZWFkIGFzICVkIiwNCj4gKwkJCXBfYmNtX2RldmljZS0+bWFudWFsX2ZjKTsNCj4gKwl9
DQo+ICsJcF9iY21fZGV2aWNlLT5iYXVkX3JhdGVfYmVmb3JlX2NvbmZpZ19kb3dubG9hZCA9IDMw
MDAwMDA7DQo+ICsJaWYgKCFvZl9wcm9wZXJ0eV9yZWFkX3UzMigNCj4gKwkJbnAsICJiYXVkLXJh
dGUtYmVmb3JlLWNvbmZpZy1kb3dubG9hZCIsDQo+ICsJCSZwX2JjbV9kZXZpY2UtPmJhdWRfcmF0
ZV9iZWZvcmVfY29uZmlnX2Rvd25sb2FkKSkgew0KPiArCQlCVF9EQkcoImJhdWQtcmF0ZS1iZWZv
cmUtY29uZmlnLWRvd25sb2FkIHJlYWQgYXMgJWQiLA0KPiArCQkJcF9iY21fZGV2aWNlLT5iYXVk
X3JhdGVfYmVmb3JlX2NvbmZpZ19kb3dubG9hZCk7DQo+ICsJfQ0KPiArCXBfYmNtX2RldmljZS0+
Y29uZmlndXJlX2F1ZGlvID0gMDsNCj4gKwlpZiAoIW9mX3Byb3BlcnR5X3JlYWRfdTMyKG5wLCAi
Y29uZmlndXJlLWF1ZGlvIiwNCj4gKwkJCQkgICZwX2JjbV9kZXZpY2UtPmNvbmZpZ3VyZV9hdWRp
bykpIHsNCj4gKwkJQlRfREJHKCJjb25maWd1cmUtYXVkaW8gcmVhZCBhcyAlZCIsDQo+ICsJCQlw
X2JjbV9kZXZpY2UtPmNvbmZpZ3VyZV9hdWRpbyk7DQo+ICsJfQ0KPiArCWlmIChwX2JjbV9kZXZp
Y2UtPmNvbmZpZ3VyZV9hdWRpbykgew0KPiArCQkvKiBEZWZhdWx0cyBmb3IgYXVkaW8gKi8NCj4g
KwkJcF9iY21fZGV2aWNlLT5QQ01DbG9ja01vZGUgPSAwOw0KPiArCQlwX2JjbV9kZXZpY2UtPlBD
TUZpbGxNZXRob2QgPSAyOw0KPiArCQlwX2JjbV9kZXZpY2UtPlBDTUZpbGxOdW0gPSAwOw0KPiAr
CQlwX2JjbV9kZXZpY2UtPlBDTUZpbGxWYWx1ZSA9IDM7DQo+ICsJCXBfYmNtX2RldmljZS0+UENN
SW5DYWxsQml0Y2xvY2sgPSAwOw0KPiArCQlwX2JjbV9kZXZpY2UtPlBDTUxTQkZpcnN0ID0gMDsN
Cj4gKwkJcF9iY21fZGV2aWNlLT5QQ01SaWdodEp1c3RpZnkgPSAwOw0KPiArCQlwX2JjbV9kZXZp
Y2UtPlBDTVJvdXRpbmcgPSAwOw0KPiArCQlwX2JjbV9kZXZpY2UtPlBDTVNob3J0RnJhbWVTeW5j
ID0gMDsNCj4gKwkJcF9iY21fZGV2aWNlLT5QQ01TeW5jTW9kZSA9IDA7DQo+ICsNCj4gKwkJaWYg
KCFvZl9wcm9wZXJ0eV9yZWFkX3UzMihucCwgIlBDTUNsb2NrTW9kZSIsDQo+ICsJCQkJCSAgJnBf
YmNtX2RldmljZS0+UENNQ2xvY2tNb2RlKSkNCj4gKwkJCUJUX0RCRygiUENNQ2xvY2tNb2RlIHJl
YWQgYXMgJWQiLA0KPiArCQkJCXBfYmNtX2RldmljZS0+UENNQ2xvY2tNb2RlKTsNCj4gKwkJaWYg
KCFvZl9wcm9wZXJ0eV9yZWFkX3UzMihucCwgIlBDTUZpbGxNZXRob2QiLA0KPiArCQkJCQkgICZw
X2JjbV9kZXZpY2UtPlBDTUZpbGxNZXRob2QpKQ0KPiArCQkJQlRfREJHKCJQQ01GaWxsTWV0aG9k
IHJlYWRhcyAlZCIsDQo+ICsJCQkJcF9iY21fZGV2aWNlLT5QQ01GaWxsTWV0aG9kKTsNCj4gKwkJ
aWYgKCFvZl9wcm9wZXJ0eV9yZWFkX3UzMihucCwgIlBDTUZpbGxOdW0iLA0KPiArCQkJCQkgICZw
X2JjbV9kZXZpY2UtPlBDTUZpbGxOdW0pKQ0KPiArCQkJQlRfREJHKCJQQ01GaWxsTnVtIHJlYWQg
YXMgJWQiLA0KPiArCQkJCXBfYmNtX2RldmljZS0+UENNRmlsbE51bSk7DQo+ICsJCWlmICghb2Zf
cHJvcGVydHlfcmVhZF91MzIobnAsICJQQ01GaWxsVmFsdWUiLA0KPiArCQkJCQkgICZwX2JjbV9k
ZXZpY2UtPlBDTUZpbGxWYWx1ZSkpDQo+ICsJCQlCVF9EQkcoIlBDTUZpbGxWYWx1ZSByZWFkIGFz
ICVkIiwNCj4gKwkJCQlwX2JjbV9kZXZpY2UtPlBDTUZpbGxWYWx1ZSk7DQo+ICsJCWlmICghb2Zf
cHJvcGVydHlfcmVhZF91MzIobnAsICJQQ01JbkNhbGxCaXRjbG9jayIsDQo+ICsJCQkJCSAgJnBf
YmNtX2RldmljZS0+UENNSW5DYWxsQml0Y2xvY2spKQ0KPiArCQkJQlRfREJHKCJQQ01JbkNhbGxC
aXRjbG9jayByZWFkIGFzICVkIiwNCj4gKwkJCQlwX2JjbV9kZXZpY2UtPlBDTUluQ2FsbEJpdGNs
b2NrKTsNCj4gKwkJaWYgKCFvZl9wcm9wZXJ0eV9yZWFkX3UzMihucCwgIlBDTUxTQkZpcnN0IiwN
Cj4gKwkJCQkJICAmcF9iY21fZGV2aWNlLT5QQ01MU0JGaXJzdCkpDQo+ICsJCQlCVF9EQkcoIlBD
TUxTQkZpcnN0IHJlYWQgYXMgJWQiLA0KPiArCQkJCXBfYmNtX2RldmljZS0+UENNTFNCRmlyc3Qp
Ow0KPiArCQlpZiAoIW9mX3Byb3BlcnR5X3JlYWRfdTMyKG5wLCAiUENNUmlnaHRKdXN0aWZ5IiwN
Cj4gKwkJCQkJICAmcF9iY21fZGV2aWNlLT5QQ01SaWdodEp1c3RpZnkpKQ0KPiArCQkJQlRfREJH
KCJQQ01SaWdodEp1c3RpZnkgcmVhZCBhcyAlZCIsDQo+ICsJCQkJcF9iY21fZGV2aWNlLT5QQ01S
aWdodEp1c3RpZnkpOw0KPiArCQlpZiAoIW9mX3Byb3BlcnR5X3JlYWRfdTMyKG5wLCAiUENNUm91
dGluZyIsDQo+ICsJCQkJCSAgJnBfYmNtX2RldmljZS0+UENNUm91dGluZykpDQo+ICsJCQlCVF9E
QkcoIlBDTVJvdXRpbmcgcmVhZCBhcyAlZCIsDQo+ICsJCQkJcF9iY21fZGV2aWNlLT5QQ01Sb3V0
aW5nKTsNCj4gKwkJaWYgKCFvZl9wcm9wZXJ0eV9yZWFkX3UzMihucCwgIlBDTVNob3J0RnJhbWVT
eW5jIiwNCj4gKwkJCQkJICAmcF9iY21fZGV2aWNlLT5QQ01TaG9ydEZyYW1lU3luYykpDQo+ICsJ
CQlCVF9EQkcoIlBDTVNob3J0RnJhbWVTeW5jIHJlYWQgYXMgJWQiLA0KPiArCQkJCXBfYmNtX2Rl
dmljZS0+UENNU2hvcnRGcmFtZVN5bmMpOw0KPiArCQlpZiAoIW9mX3Byb3BlcnR5X3JlYWRfdTMy
KG5wLCAiUENNU3luY01vZGUiLA0KPiArCQkJCQkgICZwX2JjbV9kZXZpY2UtPlBDTVN5bmNNb2Rl
KSkNCj4gKwkJCUJUX0RCRygiUENNU3luY01vZGUgcmVhZCBhcyAlZCIsDQo+ICsJCQkJcF9iY21f
ZGV2aWNlLT5QQ01TeW5jTW9kZSk7DQo+ICsJfQ0KPiArDQo+ICsJaWYgKCFvZl9wcm9wZXJ0eV9y
ZWFkX3N0cmluZyhucCwgInR0eSIsICZ0dHlfbmFtZSkpIHsNCj4gKwkJc3RyY3B5KHBfYmNtX2Rl
dmljZS0+dHR5X25hbWUsIHR0eV9uYW1lKTsNCj4gKwkJQlRfREJHKCJ0dHkgbmFtZSByZWFkIGFz
ICVzIiwgcF9iY21fZGV2aWNlLT50dHlfbmFtZSk7DQo+ICsJfQ0KPiArDQo+ICsJQlRfREJHKCJp
ZGxlVGltZW91dCBzZXQgYXMgJWQiLCBpZGxlVGltZW91dCk7DQo+ICsNCj4gKwlyZXQgPSAwOyAg
LyogSWYgd2UgbWFkZSBpdCBoZXJlLCB3ZSdyZSBmaW5lICovDQo+ICsNCj4gKwkvKiBQbGFjZSB0
aGlzIGluc3RhbmNlIG9uIHRoZSBkZXZpY2UgbGlzdCAqLw0KPiArCXNwaW5fbG9jaygmZGV2aWNl
X2xpc3RfbG9jayk7DQo+ICsJbGlzdF9hZGRfdGFpbCgmcF9iY21fZGV2aWNlLT5saXN0LCAmZGV2
aWNlX2xpc3QpOw0KPiArCXNwaW5fdW5sb2NrKCZkZXZpY2VfbGlzdF9sb2NrKTsNCj4gKw0KPiAr
ZW5kOg0KPiArCWlmIChyZXQpIHsNCj4gKwkJaWYgKHBfYmNtX2RldmljZS0+cmVnX29uX2dwaW8p
IHsNCj4gKwkJCWdwaW9kX3B1dChwX2JjbV9kZXZpY2UtPnJlZ19vbl9ncGlvKTsNCj4gKwkJCXBf
YmNtX2RldmljZS0+cmVnX29uX2dwaW8gPSBOVUxMOw0KPiArCQl9DQo+ICsJCWlmIChwX2JjbV9k
ZXZpY2UtPmJ0X3dha2VfZ3Bpbykgew0KPiArCQkJZ3Bpb2RfcHV0KHBfYmNtX2RldmljZS0+YnRf
d2FrZV9ncGlvKTsNCj4gKwkJCXBfYmNtX2RldmljZS0+YnRfd2FrZV9ncGlvID0gTlVMTDsNCj4g
KwkJfQ0KPiArCQlpZiAocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvKSB7DQo+ICsJCQlncGlv
ZF9wdXQocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvKTsNCj4gKwkJCXBfYmNtX2RldmljZS0+
ZGV2X3dha2VfZ3BpbyA9IE5VTEw7DQo+ICsJCX0NCj4gKwl9DQo+ICsNCj4gKwlCVF9EQkcoImJj
bV9idF91YXJ0X3Byb2JlIHdpdGggdGhlIHJlc3VsdCAlZCIsIHJldCk7DQo+ICsJcmV0dXJuIHJl
dDsNCj4gK30NCj4gKw0KPiArLyoNCj4gKyAqIERldmljZSBpbnN0YW5jZSByZW1vdmFsDQo+ICsg
Ki8NCj4gK3N0YXRpYyBpbnQgYmNtX2J0X3VhcnRfcmVtb3ZlKHN0cnVjdCBwbGF0Zm9ybV9kZXZp
Y2UgKnBkZXYpDQo+ICt7DQo+ICsJc3RydWN0IGJjbV9kZXZpY2UgKnBfYmNtX2RldmljZSA9IHBs
YXRmb3JtX2dldF9kcnZkYXRhKHBkZXYpOw0KPiArDQo+ICsJaWYgKHBfYmNtX2RldmljZSA9PSBO
VUxMKSB7DQo+ICsJCUJUX0RCRygiYmNtX2J0X3VhcnRfcmVtb3ZlIC0gbG9naWMgZXJyb3IsIG5v
IHByb2JlPyEiKTsNCj4gKwkJcmV0dXJuIDA7DQo+ICsJfQ0KPiArDQo+ICsJQlRfREJHKCJiY21f
YnRfdWFydF9yZW1vdmUgJXAgY29udGV4dCIsIHBfYmNtX2RldmljZSk7DQo+ICsNCj4gKwlzcGlu
X2xvY2soJmRldmljZV9saXN0X2xvY2spOw0KPiArCWxpc3RfZGVsKCZwX2JjbV9kZXZpY2UtPmxp
c3QpOw0KPiArCXNwaW5fdW5sb2NrKCZkZXZpY2VfbGlzdF9sb2NrKTsNCj4gKw0KPiArCUJUX0RC
RygiYmNtX2J0X3VhcnRfcmVtb3ZlIC0gZnJlZWluZyBpbnRlcnJ1cHQgJWQiLA0KPiArCQlwX2Jj
bV9kZXZpY2UtPmJ0X3dha2VfaXJxKTsNCj4gKwlmcmVlX2lycShwX2JjbV9kZXZpY2UtPmJ0X3dh
a2VfaXJxLCBwX2JjbV9kZXZpY2UpOw0KPiArDQo+ICsJaWYgKHBfYmNtX2RldmljZS0+cmVnX29u
X2dwaW8pIHsNCj4gKwkJQlRfREJHKCJiY21fYnRfdWFydF9yZW1vdmUgLSByZWxlYXNpbmcgcmVn
X29uX2dwaW8iKTsNCj4gKwkJZ3Bpb2RfcHV0KHBfYmNtX2RldmljZS0+cmVnX29uX2dwaW8pOw0K
PiArCQlwX2JjbV9kZXZpY2UtPnJlZ19vbl9ncGlvID0gTlVMTDsNCj4gKwl9DQo+ICsNCj4gKwlp
ZiAocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvKSB7DQo+ICsJCUJUX0RCRygiYmNtX2J0X3Vh
cnRfcmVtb3ZlIC0gcmVsZWFzaW5nIGRldl93YWtlX2dwaW8iKTsNCj4gKwkJZ3Bpb2RfcHV0KHBf
YmNtX2RldmljZS0+ZGV2X3dha2VfZ3Bpbyk7DQo+ICsJCXBfYmNtX2RldmljZS0+ZGV2X3dha2Vf
Z3BpbyA9IE5VTEw7DQo+ICsJfQ0KPiArDQo+ICsJaWYgKHBfYmNtX2RldmljZS0+YnRfd2FrZV9n
cGlvKSB7DQo+ICsJCUJUX0RCRygiYmNtX2J0X3VhcnRfcmVtb3ZlIC0gcmVsZWFzaW5nIGJ0X3dh
a2VfZ3BpbyIpOw0KPiArCQlncGlvZF9wdXQocF9iY21fZGV2aWNlLT5idF93YWtlX2dwaW8pOw0K
PiArCQlwX2JjbV9kZXZpY2UtPmJ0X3dha2VfZ3BpbyA9IE5VTEw7DQo+ICsJfQ0KPiArDQo+ICsJ
QlRfREJHKCJiY21fYnRfdWFydF9yZW1vdmUgJXAgZG9uZSIsIHBfYmNtX2RldmljZSk7DQo+ICsJ
cmV0dXJuIDA7DQo+ICt9DQo+ICsNCj4gKy8qDQo+ICsgKiBQbGF0Zm9ybSByZXN1bWUgY2FsbGJh
Y2sNCj4gKyAqLw0KPiArc3RhdGljIGludCBiY21fYnRfdWFydF9yZXN1bWUoc3RydWN0IGRldmlj
ZSAqcGRldikNCj4gK3sNCj4gKwlpbnQgcmVzdW1lX2ZsYWc7DQo+ICsJc3RydWN0IGJjbV9kZXZp
Y2UgKnBfYmNtX2RldmljZSA9IHBsYXRmb3JtX2dldF9kcnZkYXRhKA0KPiArCQl0b19wbGF0Zm9y
bV9kZXZpY2UocGRldikpOw0KPiArDQo+ICsJaWYgKHBfYmNtX2RldmljZSA9PSBOVUxMKSB7DQo+
ICsJCUJUX0RCRygiYmNtX2J0X3VhcnRfcmVzdW1lIC0gbG9naWMgZXJyb3IsIG5vIGRldmljZT8h
Iik7DQo+ICsJCXJldHVybiAwOw0KPiArCX0NCj4gKw0KPiArCUJUX0RCRygiYmNtX2J0X3VhcnRf
cmVzdW1lICVwIiwgcF9iY21fZGV2aWNlKTsNCj4gKw0KPiArCXJlc3VtZV9mbGFnID0gIXBfYmNt
X2RldmljZS0+ZGV2X3dha2VfYWN0aXZlX2xvdzsNCj4gKwlpZiAocF9iY21fZGV2aWNlLT5kZXZf
d2FrZV9ncGlvKSB7DQo+ICsJCWdwaW9kX3NldF92YWx1ZShwX2JjbV9kZXZpY2UtPmRldl93YWtl
X2dwaW8sIHJlc3VtZV9mbGFnKTsNCj4gKwkJQlRfREJHKCJiY21fYnRfdWFydF9yZXN1bWU6ICVk
IHdyaXR0ZW4sIGRlbGF5aW5nIDE1IG1zIiwNCj4gKwkJICAgICAgIHJlc3VtZV9mbGFnKTsNCj4g
KwkJbWRlbGF5KDE1KTsNCj4gKwl9DQo+ICsNCj4gKwkvKiBMZXQgdGhlIHByb3RvY29sIGtub3cg
dGhlIHBsYXRmb3JtIGlzIHJlc3VtaW5nICovDQo+ICsJaWYgKHBfYmNtX2RldmljZS0+cHJvdG9j
b2xfY2FsbGJhY2tzLnBfcmVzdW1lKQ0KPiArCQlwX2JjbV9kZXZpY2UtPnByb3RvY29sX2NhbGxi
YWNrcy5wX3Jlc3VtZSgNCj4gKwkJCXBfYmNtX2RldmljZS0+cHJvdG9jb2xfY2FsbGJhY2tzLmNv
bnRleHQpOw0KPiArDQo+ICsJcmV0dXJuIDA7DQo+ICt9DQo+ICsNCj4gKy8qDQo+ICsgKiBQbGF0
Zm9ybSBzdXNwZW5kIGNhbGxiYWNrDQo+ICsgKi8NCj4gK3N0YXRpYyBpbnQgYmNtX2J0X3VhcnRf
c3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpwZGV2KQ0KPiArew0KPiArCWludCByZXN1bWVfZmxhZzsN
Cj4gKwlzdHJ1Y3QgYmNtX2RldmljZSAqcF9iY21fZGV2aWNlID0gcGxhdGZvcm1fZ2V0X2RydmRh
dGEoDQo+ICsJCXRvX3BsYXRmb3JtX2RldmljZShwZGV2KSk7DQo+ICsNCj4gKwlpZiAocF9iY21f
ZGV2aWNlID09IE5VTEwpIHsNCj4gKwkJQlRfREJHKCJiY21fYnRfdWFydF9zdXNwZW5kIC0gbG9n
aWMgZXJyb3IsIG5vIGRldmljZT8hIik7DQo+ICsJCXJldHVybiAwOw0KPiArCX0NCj4gKw0KPiAr
CUJUX0RCRygiYmNtX2J0X3VhcnRfc3VzcGVuZCAlcCIsIHBfYmNtX2RldmljZSk7DQo+ICsNCj4g
KwkvKiBMZXQgdGhlIHByb3RvY29sIGtub3cgdGhlIHBsYXRmb3JtIGlzIHN1c3BlbmRpbmcgKi8N
Cj4gKwlpZiAocF9iY21fZGV2aWNlLT5wcm90b2NvbF9jYWxsYmFja3MucF9zdXNwZW5kKQ0KPiAr
CQlwX2JjbV9kZXZpY2UtPnByb3RvY29sX2NhbGxiYWNrcy5wX3N1c3BlbmQoDQo+ICsJCQlwX2Jj
bV9kZXZpY2UtPnByb3RvY29sX2NhbGxiYWNrcy5jb250ZXh0KTsNCj4gKw0KPiArCS8qIFN1c3Bl
bmQgdGhlIGRldmljZSAqLw0KPiArCWlmIChwX2JjbV9kZXZpY2UtPmRldl93YWtlX2dwaW8pIHsN
Cj4gKwkJcmVzdW1lX2ZsYWcgPSAhcF9iY21fZGV2aWNlLT5kZXZfd2FrZV9hY3RpdmVfbG93Ow0K
PiArCQlncGlvZF9zZXRfdmFsdWUocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvLCAhcmVzdW1l
X2ZsYWcpOw0KPiArCQlCVF9EQkcoImJjbV9idF91YXJ0X3N1c3BlbmQ6ICVkIHdyaXR0ZW4sIGRl
bGF5aW5nIDE1IG1zIiwNCj4gKwkJCSFyZXN1bWVfZmxhZyk7DQo+ICsJCW1kZWxheSgxNSk7DQo+
ICsJfQ0KPiArDQo+ICsJcmV0dXJuIDA7DQo+ICt9DQo+ICsNCj4gKy8qDQo+ICsgKiBFbnRyeSBw
b2ludCBmb3IgY2FsbHMgZnJvbSB0aGUgcHJvdG9jb2wNCj4gKyAqLw0KPiAraW50IGJ0YmNtX3Vh
cnRfY29udHJvbChpbnQgYWN0aW9uLCB2b2lkICpkZXZpY2VfY29udGV4dCwNCj4gKwl2b2lkICpw
X2RhdGEsIHVuc2lnbmVkIGxvbmcgKnBfc2l6ZSkNCj4gK3sNCj4gKwlzdHJ1Y3QgYnRiY21fdWFy
dF9jYWxsYmFja3MgKnBjOw0KPiArCXN0cnVjdCBidGJjbV91YXJ0X3BhcmFtZXRlcnMgKnBwID0g
cF9kYXRhOyAvKiBmb3IgcGFycyBhY3Rpb24gb25seSAqLw0KPiArCWludCByZXQgPSAwOw0KPiAr
CWludCByZXN1bWVfZmxhZywgcG93ZXJvbl9mbGFnOw0KDQpGb3IgdHJ1ZSBib29sZWFuIHZhcmlh
YmxlLCBwbGVhc2UgdXNlIGJvb2wgaW5zdGVhZCBvZiBpbnQuDQoNCklGOiBUaGVzZSBhcmUgYWN0
dWFsbHkgcGFyYW1ldGVycyBmb3IgdGhlIEdQSU8gY2FsbHMgc28gdGhleSBzaG91bGQgYmUgImlu
dCIuDQoNCj4gKwlzdHJ1Y3QgYmNtX2RldmljZSAqcF9iY21fZGV2aWNlID0gZGV2aWNlX2NvbnRl
eHQ7DQo+ICsJc3RydWN0IGxpc3RfaGVhZCAqcHRyOw0KPiArCWJvb2wgaXNfZm91bmQgPSBmYWxz
ZTsNCj4gKw0KPiArCS8qIFNwZWNpYWwgcHJvY2Vzc2luZyBmb3IgdGhlIGNhbGxiYWNrIGNvbmZp
Z3VyYXRpb24gKi8NCj4gKwlpZiAoYWN0aW9uID09IEJUQkNNX1VBUlRfQUNUSU9OX0NPTkZJR1VS
RV9DQUxMQkFDS1MpIHsNCj4gKwkJcGMgPSBwX2RhdGE7DQo+ICsNCj4gKwkJQlRfREJHKCJidGJj
bV91YXJ0X2NvbnRyb2wgLSBjb25maWd1cmUgY2FsbGJhY2tzIik7DQo+ICsJCWlmICgocF9kYXRh
ID09IE5VTEwpIHx8ICpwX3NpemUgIT0gc2l6ZW9mKHN0cnVjdA0KDQpFeHRyYSAoKSBhcm91bmQg
eCA9PSB5IGFyZSBub3QgbmVlZGVkLiBEbyBib3RoZXIgd2l0aCB0aGVtLg0KIA0KSUY6IENoYW5n
ZWQuDQoNCj4gKwkJCWJ0YmNtX3VhcnRfY2FsbGJhY2tzKSB8fCAocGMtPmludGVyZmFjZV92ZXJz
aW9uICE9DQo+ICsJCQlCVEJDTV9VQVJUX0lOVEVSRkFDRV9WRVJTSU9OKSkgew0KDQpXcm9uZyBp
bmRlbnRhdGlvbi4NCg0KSUY6IENoYW5nZWQuDQoNCj4gKwkJCUJUX0RCRygiYnRiY21fdWFydF9j
b250cm9sIC0gY2FsbGJhY2tzIG1pc21hdGNoISIpOw0KPiArCQkJcmV0dXJuIC1FMkJJRzsNCj4g
KwkJfQ0KPiArDQo+ICsJCUJUX0RCRygiYnRiY21fdWFydF9jb250cm9sIC0gY29uZmlndXJlIGNh
bGxiYWNrcyBmb3IgJXMoJXApIiwNCj4gKwkJICAgICAgIHBjLT5uYW1lLCBwYy0+Y29udGV4dCk7
DQo+ICsJCWlmIChwX2JjbV9kZXZpY2UgPT0gTlVMTCkgew0KPiArCQkJc3Bpbl9sb2NrKCZkZXZp
Y2VfbGlzdF9sb2NrKTsNCj4gKwkJCWxpc3RfZm9yX2VhY2gocHRyLCAmZGV2aWNlX2xpc3QpIHsN
Cj4gKwkJCQlwX2JjbV9kZXZpY2UgPSBsaXN0X2VudHJ5KHB0ciwgc3RydWN0DQo+ICsJCQkJCQkJ
ICBiY21fZGV2aWNlLCBsaXN0KTsNCj4gKwkJCQlpZiAoIXN0cmNtcChwX2JjbV9kZXZpY2UtPnR0
eV9uYW1lLCBwYy0+bmFtZSkpIHsNCj4gKwkJCQkJaXNfZm91bmQgPSB0cnVlOw0KPiArCQkJCQli
cmVhazsNCj4gKwkJCQl9DQo+ICsJCQl9DQo+ICsNCj4gKwkJCXNwaW5fdW5sb2NrKCZkZXZpY2Vf
bGlzdF9sb2NrKTsNCj4gKwkJCWlmICghaXNfZm91bmQpIHsNCj4gKwkJCQlCVF9EQkcoImJ0YmNt
X3VhcnRfY29udHJvbCAtIG5vIGRldmljZSEiKTsNCj4gKwkJCQlyZXR1cm4gLUVOT0VOVDsNCj4g
KwkJCX0NCj4gKwkJfQ0KPiArDQo+ICsJCXBfYmNtX2RldmljZS0+cHJvdG9jb2xfY2FsbGJhY2tz
ID0gKnBjOw0KPiArCQltZW1jcHkocF9kYXRhLCAmcF9iY21fZGV2aWNlLCBzaXplb2YocF9iY21f
ZGV2aWNlKSk7DQo+ICsJCSpwX3NpemUgPSBzaXplb2YocF9iY21fZGV2aWNlKTsNCj4gKwkJcmV0
dXJuIHJldDsNCj4gKwl9DQo+ICsNCj4gKwkvKiBBbGwgb3RoZXIgcmVxdWVzdHMgbXVzdCBoYXZl
IHRoZSByaWdodCBjb250ZXh0ICovDQo+ICsJaWYgKHBfYmNtX2RldmljZSA9PSBOVUxMKSB7DQo+
ICsJCUJUX0RCRygiYnRiY21fdWFydF9jb250cm9sIC0gZmFpbGluZywgbm8gZGV2aWNlIik7DQo+
ICsJCXJldHVybiAtRU5PRU5UOw0KPiArCX0NCj4gKw0KPiArCXN3aXRjaCAoYWN0aW9uKSB7DQo+
ICsJY2FzZSBCVEJDTV9VQVJUX0FDVElPTl9QT1dFUl9PTjoNCj4gKwkJQlRfREJHKCJidGJjbV91
YXJ0X2NvbnRyb2wgJXAgLSBwb3dlciBvbiIsIGRldmljZV9jb250ZXh0KTsNCj4gKwkJaWYgKHBf
YmNtX2RldmljZS0+cmVnX29uX2dwaW8pIHsNCj4gKwkJCXBvd2Vyb25fZmxhZyA9ICFwX2JjbV9k
ZXZpY2UtPnJlZ19vbl9hY3RpdmVfbG93Ow0KPiArCQkJZ3Bpb2Rfc2V0X3ZhbHVlKHBfYmNtX2Rl
dmljZS0+cmVnX29uX2dwaW8sDQo+ICsJCQkJCXBvd2Vyb25fZmxhZyk7DQo+ICsJCQlCVF9EQkco
ImJ0YmNtX3VhcnRfY29udHJvbCAtIHB3cm9uICVkLCBkZWxheSAxNSBtcyIsDQo+ICsJCQkgICAg
ICAgcG93ZXJvbl9mbGFnKTsNCj4gKwkJCW1kZWxheSgxNSk7DQo+ICsJCX0NCj4gKwkJYnJlYWs7
DQo+ICsNCj4gKwljYXNlIEJUQkNNX1VBUlRfQUNUSU9OX1BPV0VSX09GRjoNCj4gKwkJQlRfREJH
KCJidGJjbV91YXJ0X2NvbnRyb2wgJXAgLSBwb3dlciBvZmYiLCBkZXZpY2VfY29udGV4dCk7DQo+
ICsJCWlmIChwX2JjbV9kZXZpY2UtPnJlZ19vbl9ncGlvKSB7DQo+ICsJCQlwb3dlcm9uX2ZsYWcg
PSBwX2JjbV9kZXZpY2UtPnJlZ19vbl9hY3RpdmVfbG93Ow0KPiArCQkJZ3Bpb2Rfc2V0X3ZhbHVl
KHBfYmNtX2RldmljZS0+cmVnX29uX2dwaW8sDQo+ICsJCQkJCXBvd2Vyb25fZmxhZyk7DQo+ICsJ
CQlCVF9EQkcoImJ0YmNtX3VhcnRfY29udHJvbCAtIHB3cm9mZiAlZCwgZGVsYXkgMTUgbXMiLA0K
PiArCQkJICAgICAgIHBvd2Vyb25fZmxhZyk7DQo+ICsJCQltZGVsYXkoMTUpOw0KPiArCQl9DQo+
ICsJCWJyZWFrOw0KPiArDQo+ICsJY2FzZSBCVEJDTV9VQVJUX0FDVElPTl9SRVNVTUU6DQo+ICsJ
CUJUX0RCRygiYnRiY21fdWFydF9jb250cm9sICVwIC0gcmVzdW1lIiwgZGV2aWNlX2NvbnRleHQp
Ow0KPiArCQlpZiAocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvKSB7DQo+ICsJCQlyZXN1bWVf
ZmxhZyA9ICFwX2JjbV9kZXZpY2UtPmRldl93YWtlX2FjdGl2ZV9sb3c7DQo+ICsJCQlncGlvZF9z
ZXRfdmFsdWUocF9iY21fZGV2aWNlLT5kZXZfd2FrZV9ncGlvLA0KPiArCQkJCQlyZXN1bWVfZmxh
Zyk7DQo+ICsJCQlCVF9EQkcoImJ0YmNtX3VhcnRfY29udHJvbCAtIHJlc3VtZSAlZCwgZGVsYXkg
MTUgbXMiLA0KPiArCQkJICAgICAgIHJlc3VtZV9mbGFnKTsNCj4gKwkJCW1kZWxheSgxNSk7DQo+
ICsJCX0NCj4gKwkJYnJlYWs7DQo+ICsNCj4gKwljYXNlIEJUQkNNX1VBUlRfQUNUSU9OX1NVU1BF
TkQ6DQo+ICsJCUJUX0RCRygiYnRiY21fdWFydF9jb250cm9sICVwIC0gc3VzcGVuZCIsIGRldmlj
ZV9jb250ZXh0KTsNCj4gKwkJaWYgKHBfYmNtX2RldmljZS0+ZGV2X3dha2VfZ3Bpbykgew0KPiAr
CQkJcmVzdW1lX2ZsYWcgPSAhcF9iY21fZGV2aWNlLT5kZXZfd2FrZV9hY3RpdmVfbG93Ow0KPiAr
CQkJZ3Bpb2Rfc2V0X3ZhbHVlKHBfYmNtX2RldmljZS0+ZGV2X3dha2VfZ3BpbywNCj4gKwkJCQkJ
IXJlc3VtZV9mbGFnKTsNCj4gKwkJCUJUX0RCRygiYnRiY21fdWFydF9jb250cm9sIC0gc3VzcGVu
ZCAlZCwgZGVsYXkgMTVtcyIsDQo+ICsJCQkgICAgICAgIXJlc3VtZV9mbGFnKTsNCj4gKwkJCW1k
ZWxheSgxNSk7DQo+ICsJCX0NCj4gKwkJYnJlYWs7DQo+ICsNCj4gKwljYXNlIEJUQkNNX1VBUlRf
QUNUSU9OX0dFVF9QQVJBTUVURVJTOg0KPiArCQlCVF9EQkcoImJ0YmNtX3VhcnRfY29udHJvbCAl
cCAtIGdldCBwYXJzIiwgZGV2aWNlX2NvbnRleHQpOw0KPiArCQlpZiAoKHBfZGF0YSA9PSBOVUxM
KSB8fA0KPiArCQkJKCpwX3NpemUgPCBzaXplb2Yoc3RydWN0IGJ0YmNtX3VhcnRfcGFyYW1ldGVy
cykpKSB7DQo+ICsJCQlCVF9EQkcoImJ0YmNtX3VhcnRfY29udHJvbCAtIGZhaWxpbmcsIHdyb25n
IHBhciBzaXplIik7DQo+ICsJCQlyZXR1cm4gLUUyQklHOw0KPiArCQl9DQo+ICsNCj4gKwkJbWVt
c2V0KHBwLCAwLCBzaXplb2Yoc3RydWN0IGJ0YmNtX3VhcnRfcGFyYW1ldGVycykpOw0KPiArCQlw
cC0+aW50ZXJmYWNlX3ZlcnNpb24gPSBCVEJDTV9VQVJUX0lOVEVSRkFDRV9WRVJTSU9OOw0KPiAr
CQlwcC0+Y29uZmlndXJlX3NsZWVwID0gcF9iY21fZGV2aWNlLT5jb25maWd1cmVfc2xlZXA7DQo+
ICsJCXBwLT5tYW51YWxfZmMgPSBwX2JjbV9kZXZpY2UtPm1hbnVhbF9mYzsNCj4gKwkJcHAtPmRl
dl93YWtlX2FjdGl2ZV9sb3cgPSBwX2JjbV9kZXZpY2UtPmRldl93YWtlX2FjdGl2ZV9sb3c7DQo+
ICsJCXBwLT5idF93YWtlX2FjdGl2ZV9sb3cgPSBwX2JjbV9kZXZpY2UtPmJ0X3dha2VfYWN0aXZl
X2xvdzsNCj4gKwkJcHAtPmlkbGVfdGltZW91dF9pbl9zZWNzID0gaWRsZVRpbWVvdXQ7DQo+ICsJ
CXBwLT5iYXVkX3JhdGVfYmVmb3JlX2NvbmZpZ19kb3dubG9hZCA9DQo+ICsJCQlwX2JjbV9kZXZp
Y2UtPmJhdWRfcmF0ZV9iZWZvcmVfY29uZmlnX2Rvd25sb2FkOw0KPiArCQlwcC0+Y29uZmlndXJl
X2F1ZGlvID0gcF9iY21fZGV2aWNlLT5jb25maWd1cmVfYXVkaW87DQo+ICsJCXBwLT5QQ01DbG9j
a01vZGUgPSBwX2JjbV9kZXZpY2UtPlBDTUNsb2NrTW9kZTsNCj4gKwkJcHAtPlBDTUZpbGxNZXRo
b2QgPSBwX2JjbV9kZXZpY2UtPlBDTUZpbGxNZXRob2Q7DQo+ICsJCXBwLT5QQ01GaWxsTnVtID0g
cF9iY21fZGV2aWNlLT5QQ01GaWxsTnVtOw0KPiArCQlwcC0+UENNRmlsbFZhbHVlID0gcF9iY21f
ZGV2aWNlLT5QQ01GaWxsVmFsdWU7DQo+ICsJCXBwLT5QQ01JbkNhbGxCaXRjbG9jayA9IHBfYmNt
X2RldmljZS0+UENNSW5DYWxsQml0Y2xvY2s7DQo+ICsJCXBwLT5QQ01MU0JGaXJzdCA9IHBfYmNt
X2RldmljZS0+UENNTFNCRmlyc3Q7DQo+ICsJCXBwLT5QQ01SaWdodEp1c3RpZnkgPSBwX2JjbV9k
ZXZpY2UtPlBDTVJpZ2h0SnVzdGlmeTsNCj4gKwkJcHAtPlBDTVJvdXRpbmcgPSBwX2JjbV9kZXZp
Y2UtPlBDTVJvdXRpbmc7DQo+ICsJCXBwLT5QQ01TaG9ydEZyYW1lU3luYyA9IHBfYmNtX2Rldmlj
ZS0+UENNU2hvcnRGcmFtZVN5bmM7DQo+ICsJCXBwLT5QQ01TeW5jTW9kZSA9IHBfYmNtX2Rldmlj
ZS0+UENNU3luY01vZGU7DQo+ICsJCSpwX3NpemUgPSBzaXplb2Yoc3RydWN0IGJ0YmNtX3VhcnRf
cGFyYW1ldGVycyk7DQo+ICsJCWJyZWFrOw0KPiArDQo+ICsJZGVmYXVsdDoNCj4gKwkJQlRfREJH
KCJidGJjbV91YXJ0X2NvbnRyb2wgJXAgdW5rbm93biBhY3QgJWQiLA0KPiArCQkgICAgICAgZGV2
aWNlX2NvbnRleHQsIGFjdGlvbik7DQo+ICsJCXJldCA9IC1FSU5WQUw7DQo+ICsJCWJyZWFrOw0K
PiArCX0NCj4gKw0KPiArCXJldHVybiByZXQ7DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9MKGJ0YmNt
X3VhcnRfY29udHJvbCk7DQo+ICsNCj4gKy8qIFBsYXRmb3JtIHN1c3AgYW5kIHJlc3VtZSBjYWxs
YmFja3MgKi8NCj4gK3N0YXRpYyBTSU1QTEVfREVWX1BNX09QUyhiY21fYnRfdWFydF9wbV9vcHMs
DQo+ICsJYmNtX2J0X3VhcnRfc3VzcGVuZCwgYmNtX2J0X3VhcnRfcmVzdW1lKTsNCj4gKw0KPiAr
LyogRHJpdmVyIG1hdGNoIHRhYmxlICovDQo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2Rldmlj
ZV9pZCBiY21fYnRfdWFydF9tYXRjaF90YWJsZVtdID0gew0KDQpObyBuZWVkIHRvIGNhbGwgaXQg
bWF0Y2hfdGFibGUuIEp1c3QgdGFibGUgYXMgc3VmZmljZSBpcyBmaW5lLg0KDQpJRjogQ2hhbmdl
ZC4NCg0KPiArCXsgLmNvbXBhdGlibGUgPSAiYnJjbSxicmNtLWJ0LXVhcnQiIH0sDQo+ICsJe30N
Cj4gK307DQo+ICsNCj4gKy8qIERyaXZlciBjb25maWd1cmF0aW9uICovDQo+ICtzdGF0aWMgc3Ry
dWN0IHBsYXRmb3JtX2RyaXZlciBiY21fYnRfdWFydF9wbGF0Zm9ybV9kcml2ZXIgPSB7DQoNCk5v
IG5lZWQgdG8gY2FsbCBpdCBwbGF0Zm9ybV9kcml2ZXIuIEp1c3QgZHJpdmVyIGFzIHN1ZmZpeCBp
cyBmaW5lLg0KDQpJRjogQ2hhbmdlZC4NCg0KPiArCS5wcm9iZSA9IGJjbV9idF91YXJ0X3Byb2Jl
LA0KPiArCS5yZW1vdmUgPSBiY21fYnRfdWFydF9yZW1vdmUsDQo+ICsJLmRyaXZlciA9IHsNCj4g
KwkJLm5hbWUgPSAiYnJjbV9idF91YXJ0IiwNCj4gKwkJLm9mX21hdGNoX3RhYmxlID0gb2ZfbWF0
Y2hfcHRyKGJjbV9idF91YXJ0X21hdGNoX3RhYmxlKSwNCj4gKwkJLm93bmVyID0gVEhJU19NT0RV
TEUsDQo+ICsJCS5wbSA9ICZiY21fYnRfdWFydF9wbV9vcHMsDQo+ICsJfSwNCj4gK307DQo+ICsN
Cj4gK21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIoYmNtX2J0X3VhcnRfcGxhdGZvcm1fZHJpdmVyKTsN
Cj4gKw0KPiArTU9EVUxFX0FVVEhPUigiSWx5YSBGYWVuc29uIik7DQo+ICtNT0RVTEVfREVTQ1JJ
UFRJT04oIkJyb2FkY29tIEJsdWV0b290aCBVQVJUIERyaXZlciIpOw0KPiArTU9EVUxFX0xJQ0VO
U0UoIkR1YWwgQlNEL0dQTOKAnSk7DQoNCkNvcHlyaWdodCBoZWFkZXIgc2F5cyBHUEx2Mi4gU28g
bGV0cyByZWZsZWN0IHRoZSBjb3JyZWN0IGxpY2Vuc2UgaGVyZS4NCg0KSUY6IFRoZSBjb3B5cmln
aHQgaGVhZGVyIHVwZGF0ZWQgdG8gbWF0Y2ggdGhlIGR1YWwgQlNEL0dQTCBsaWNlbnNlLg0KDQo+
ICsNCj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvYmx1ZXRvb3RoL2J0YmNtX3VhcnQuaCBiL2RyaXZl
cnMvYmx1ZXRvb3RoL2J0YmNtX3VhcnQuaA0KPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiBpbmRl
eCAwMDAwMDAwLi41ODAxNzUzDQo+IC0tLSAvZGV2L251bGwNCj4gKysrIGIvZHJpdmVycy9ibHVl
dG9vdGgvYnRiY21fdWFydC5oDQo+IEBAIC0wLDAgKzEsOTAgQEANCj4gKy8qDQo+ICsgKg0KPiAr
ICogIEJsdWV0b290aCBCQ00gVUFSVCBEcml2ZXIgSGVhZGVyDQo+ICsgKg0KPiArICogIENvcHly
aWdodCAoYykgMjAxNSBCcm9hZGNvbSBDb3Jwb3JhdGlvbg0KPiArICoNCj4gKyAqICBUaGlzIHBy
b2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1v
ZGlmeQ0KPiArICogIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGlj
IExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5DQo+ICsgKiAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRh
dGlvbjsgZWl0aGVyIHZlcnNpb24gMiBvZiB0aGUgTGljZW5zZSwgb3INCj4gKyAqICAoYXQgeW91
ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLg0KPiArICoNCj4gKyAqICBUaGlzIHByb2dyYW0g
aXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwNCj4gKyAq
ICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJy
YW50eSBvZg0KPiArICogIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VM
QVIgUFVSUE9TRS4gIFNlZSB0aGUNCj4gKyAqICBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBm
b3IgbW9yZSBkZXRhaWxzLg0KPiArICoNCj4gKyAqLw0KPiArI2lmbmRlZiBCVEJDTV9VQVJUX0gN
Cj4gKyNkZWZpbmUgQlRCQ01fVUFSVF9IDQo+ICsNCj4gKy8qIENoYW5nZSB0aGUgdmVyc2lvbiBp
ZiB5b3UgY2hhbmdlIGFueXRoaW5nIGluIHRoaXMgaGVhZGVyICovDQo+ICsjZGVmaW5lIEJUQkNN
X1VBUlRfSU5URVJGQUNFX1ZFUlNJT04gMQ0KDQpJIGRvIG5vdCBzZWUgdGhlIG5lZWQgZm9yIHRo
aXMuIFdoeSB3b3VsZCB3ZSBkbyB0aGF0Pw0KDQpJRjogU2luY2UgdGhpcyBpcyB0aGUgaW50ZXIt
bW9kdWxlIGludGVyZmFjZSwgbWlzbWF0Y2hlZCBiaW5hcmllcyBtYXkNCnJ1biBvbiBhIHBsYXRm
b3JtIHdoZW4gb25lIHNpZGUncyBpbnRlcmZhY2UgaXMgbmV3ZXIgdGhhbiB0aGUgb3RoZXIuDQpU
aGUgZHJpdmVyIGNoZWNrcyB0aGF0IHRoZSBjYWxsZXIgcnVucyB0aGUgcmlnaHQgaW50ZXJmYWNl
IHZlcnNpb24gYmVmb3JlDQphbGxvd2luZyB0aGUgY2FsbGVyJ3MgcmVxdWVzdHMuDQoNCj4gKw0K
PiArLyogQ2FsbGJhY2tzIGZyb20gdGhlIGRyaXZlciBpbnRvIHRoZSBwcm90b2NvbCAqLw0KPiAr
dHlwZWRlZiB2b2lkICgqcF9zdXNwZW5kX2NhbGxiYWNrKSh2b2lkICpjb250ZXh0KTsNCj4gK3R5
cGVkZWYgdm9pZCAoKnBfcmVzdW1lX2NhbGxiYWNrKSh2b2lkICpjb250ZXh0KTsNCj4gK3R5cGVk
ZWYgdm9pZCAoKnBfd2FrZXVwX2NhbGxiYWNrKSh2b2lkICpjb250ZXh0KTsNCg0KRXh0cmEgZW1w
dHkgbGluZSBoZXJlLg0KDQpJRjogIEV4dHJhIGxpbmUgcmVtb3ZlZC4NCg0KPiArc3RydWN0IGJ0
YmNtX3VhcnRfY2FsbGJhY2tzIHsNCj4gKwlpbnQgaW50ZXJmYWNlX3ZlcnNpb247IC8qIGludGVy
ZmFjZSAjIGNvbXBpbGVkIGFnYWluc3QgKi8NCj4gKwl2b2lkICpjb250ZXh0OyAgICAgICAgIC8q
IHByb3RvY29sIGluc3RhbmNlIGNvbnRleHQgKi8NCj4gKwljaGFyIG5hbWVbNjRdOyAgICAgICAg
IC8qIHByb3RvY29sIHR0eSBkZXZpY2UsIGZvciBleGFtcGxlLCB0dHlTMCAqLw0KDQpXaHkgd291
bGQgd2UgcmVjb3JkIHRoZSBUVFkgbmFtZS4gSXQgbWlnaHQgYWN0dWFsbHkgY2hhbmdlLg0KDQpJ
RjogWW91J3ZlIHByZXZpb3VzbHkgYXNrZWQgdG8gc3VwcG9ydCBtdWx0aXBsZSBCVCBVQVJUIGRl
dmljZSBpbnN0YW5jZXMuDQpCb3RoIEJsdWVaIHByb3RvY29sIGFuZCB0aGUgZGV2aWNlIHVzZSB0
aGUgdHR5IG5hbWUgdG8gYmluZCB0aGUgcmlnaHQNCnByb3RvY29sIGluc3RhbmNlIGludG8gdGhl
IHJpZ2h0IGRldmljZSBpbnN0YW5jZS4gSSdsbCBnbGFkbHkgcmVtb3ZlIHRoZQ0KdHR5IG5hbWUg
aWYgbXVsdGlwbGUgQlQgVUFSVCBzdXBwb3J0IGlzIG5vdCBuZWVkZWQuDQoNCj4gKw0KPiArCS8q
IENhbGxiYWNrcyBwcm9wZXIgKi8NCj4gKwlwX3N1c3BlbmRfY2FsbGJhY2sgcF9zdXNwZW5kOw0K
PiArCXBfcmVzdW1lX2NhbGxiYWNrIHBfcmVzdW1lOw0KPiArCXBfd2FrZXVwX2NhbGxiYWNrIHBf
d2FrZXVwOw0KDQpJIGRvIG5vdCBnZXQgdGhpcyBwXyBwcmVmaXggbmFtaW5nLiBXaGF0IGlzIHRo
YXQgZm9yPw0KDQpJRjogVGhlc2UgYXJlIHBvaW50ZXJzIHRvIHRoZSBwcm90b2NvbCBmdW5jdGlv
bnMgY2FsbGVkIGJ5IHRoZSBkcml2ZXIgdXBvbg0KcGxhdGZvcm0gc3VzcGVuZC9yZXN1bWUgYW5k
IGRldmljZSB3YWtldXAgaW50ZXJydXB0Lg0KDQo+ICt9Ow0KPiArDQo+ICsvKiBEcml2ZXIgcGFy
YW1ldGVycyByZXRyaWV2ZWQgZnJvbSB0aGUgRFQgb3IgQUNQSSAqLw0KPiArc3RydWN0IGJ0YmNt
X3VhcnRfcGFyYW1ldGVycyB7DQo+ICsJaW50IGludGVyZmFjZV92ZXJzaW9uOyAvKiBpbnRlcmZh
Y2UgIyBjb21waWxlZCBhZ2FpbnN0ICovDQo+ICsNCj4gKwkvKiBQYXJhbWV0ZXJzIHByb3BlciAq
Lw0KDQpXaGF0IGlzIGEg4oCccHJvcGVy4oCdPw0KDQpJRjogQ2hhbmdlZCB0aGUgY29tbWVudCB0
byByZWFkICJQYXJhbWV0ZXJzIHRoZW1zZWx2ZXMiLiBUaGF0IHdhcw0KSW4gcmVmZXJlbmNlIHRv
IHRoZSBmaXJzdCBtZW1iZXIgb2YgdGhhdCBzdHJ1Y3R1cmUgd2hpY2ggd2FzIG5vdCBhDQpQYXJh
bWV0ZXIuDQoNCj4gKwlpbnQgY29uZmlndXJlX3NsZWVwOw0KPiArCWludCBtYW51YWxfZmM7DQo+
ICsJaW50IGRldl93YWtlX2FjdGl2ZV9sb3c7DQo+ICsJaW50IGJ0X3dha2VfYWN0aXZlX2xvdzsN
Cj4gKwlpbnQgaWRsZV90aW1lb3V0X2luX3NlY3M7DQo+ICsJaW50IGJhdWRfcmF0ZV9iZWZvcmVf
Y29uZmlnX2Rvd25sb2FkOw0KPiArCWludCBjb25maWd1cmVfYXVkaW87DQo+ICsJaW50IFBDTUNs
b2NrTW9kZTsNCj4gKwlpbnQgUENNRmlsbE1ldGhvZDsNCj4gKwlpbnQgUENNRmlsbE51bTsNCj4g
KwlpbnQgUENNRmlsbFZhbHVlOw0KPiArCWludCBQQ01JbkNhbGxCaXRjbG9jazsNCj4gKwlpbnQg
UENNTFNCRmlyc3Q7DQo+ICsJaW50IFBDTVJpZ2h0SnVzdGlmeTsNCj4gKwlpbnQgUENNUm91dGlu
ZzsNCj4gKwlpbnQgUENNU2hvcnRGcmFtZVN5bmM7DQo+ICsJaW50IFBDTVN5bmNNb2RlOw0KPiAr
fTsNCj4gKw0KPiArLyoNCj4gKyAqIEFjdGlvbnMgb24gdGhlIEJUQkNNX1VBUlQgZHJpdmVyDQo+
ICsgKi8NCj4gKw0KPiArLyogQ29uZmlndXJlIHByb3RvY29sIGNhbGxiYWNrcyAqLw0KPiArI2Rl
ZmluZSBCVEJDTV9VQVJUX0FDVElPTl9DT05GSUdVUkVfQ0FMTEJBQ0tTIDANCj4gKw0KPiArLyog
UmV0cmlldmUgQlQgZGV2aWNlIHBhcmFtZXRlcnMgKi8NCj4gKyNkZWZpbmUgQlRCQ01fVUFSVF9B
Q1RJT05fR0VUX1BBUkFNRVRFUlMgICAgICAxDQo+ICsNCj4gKy8qIFJlc3VtZSB0aGUgQlQgZGV2
aWNlIHZpYSBHUElPICovDQo+ICsjZGVmaW5lIEJUQkNNX1VBUlRfQUNUSU9OX1JFU1VNRSAgICAg
ICAgICAgICAgMg0KPiArDQo+ICsvKiBTdXNwZW5kIHRoZSBCVCBkZXZpY2UgdmlhIEdQSU8gKi8N
Cj4gKyNkZWZpbmUgQlRCQ01fVUFSVF9BQ1RJT05fU1VTUEVORCAgICAgICAgICAgICAzDQo+ICsN
Cj4gKy8qIFBvd2VyIHRoZSBCVCBkZXZpY2Ugb2ZmIHZpYSBHUElPICovDQo+ICsjZGVmaW5lIEJU
QkNNX1VBUlRfQUNUSU9OX1BPV0VSX09GRiAgICAgICAgICAgNA0KPiArDQo+ICsvKiBQb3dlciB0
aGUgQlQgZGV2aWNlIG9uIHZpYSBHUElPICovDQo+ICsjZGVmaW5lIEJUQkNNX1VBUlRfQUNUSU9O
X1BPV0VSX09OICAgICAgICAgICAgNQ0KPiArDQo+ICsvKiBFeGVjdXRlIGFuIGFjdGlvbiBvbiB0
aGUgQlQgZGV2aWNlICovDQo+ICtleHRlcm4gaW50IGJ0YmNtX3VhcnRfY29udHJvbChpbnQgYWN0
aW9uLCB2b2lkICpkZXZpY2VfY29udGV4dCwNCj4gKwkJCSAgICAgIHZvaWQgKnBfZGF0YSwgdW5z
aWduZWQgbG9uZyAqcF9zaXplKTsNCg0KQ2FuIHRoZXNlIGJlIDYgaW5kaXZpZHVhbCBmdW5jdGlv
biBpbnN0ZWFkIG9mIHRyeWluZyB0byBmaWRkbGUgdGhpcyB0aHJvdWdoIGEgc2luZ2xlIG9uZT8N
Cg0KSUY6IFRoZXkgY291bGQgYmUuIEhvd2V2ZXIsIHRoaXMgaXMgYSBkcml2ZXIgc28gdGhlIGNs
ZWFuZXN0IGludGVyZmFjZSB0byBpdCB3b3VsZA0KYXJndWFibHkgYmUgdGhyb3VnaCB0aGUgaW9j
dGxzLiBJdCBtYXkgc3RpbGwgYmVjb21lIGFuIGlvY3RsIGF0IHNvbWUgcG9pbnQgYWxsb3dpbmcN
CmZvciBib3RoIHVzZXIgYW5kIGtlcm5lbCBtb2RlIGFjY2Vzcy4gVGhpcyBmdW5jdGlvbiB3b3Vs
ZCB0aGVuIGJlIGVhc2lseSB1cGRhdGVkDQp0byBzZXJ2ZSBpb2N0bHMuDQoNCj4gKw0KPiArI2Vu
ZGlmDQo+ICsNCj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvYmx1ZXRvb3RoL2hjaV9iY20uYyBiL2Ry
aXZlcnMvYmx1ZXRvb3RoL2hjaV9iY20uYw0KPiBpbmRleCAxZWMwYjRhLi5lNzBmODliIDEwMDY0
NA0KPiAtLS0gYS9kcml2ZXJzL2JsdWV0b290aC9oY2lfYmNtLmMNCj4gKysrIGIvZHJpdmVycy9i
bHVldG9vdGgvaGNpX2JjbS5jDQo+IEBAIC0xLDggKzEsMTMgQEANCj4gLyoNCj4gICoNCj4gLSAq
ICBCbHVldG9vdGggSENJIFVBUlQgZHJpdmVyIGZvciBCcm9hZGNvbSBkZXZpY2VzDQo+ICsgKiAg
Qmx1ZXRvb3RoIFVBUlQgSDQgcHJvdG9jb2wgZm9yIEJyb2FkY29tIGRldmljZXMNCj4gICoNCj4g
LSAqICBDb3B5cmlnaHQgKEMpIDIwMTUgIEludGVsIENvcnBvcmF0aW9uDQo+ICsgKiAgQ29weXJp
Z2h0IChjKSAyMDE1IEludGVsIENvcnBvcmF0aW9uDQo+ICsgKiAgQ29weXJpZ2h0IChjKSAyMDE1
IEJyb2FkY29tIENvcnBvcmF0aW9uDQo+ICsgKg0KPiArICogIEFja25vd2xlZGdlbWVudHM6DQo+
ICsgKiAgVGhpcyBmaWxlIGhhcyBiZWVuIGJhc2VkIG9uIGhjaV9oNC5jIG9yaWdpbmFsbHkgZGV2
ZWxvcGVkDQo+ICsgKiAgYnkgTWF4aW0gS3Jhc255YW5za3kgYW5kIE1hcmNlbCBIb2x0bWFubi4N
Cj4gICoNCj4gICoNCj4gICogIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2Fu
IHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5DQo+IEBAIC0xNSwxMzkgKzIwLDg0MiBAQA0K
PiAgKiAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NF
LiAgU2VlIHRoZQ0KPiAgKiAgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0
YWlscy4NCj4gICoNCj4gLSAqICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRo
ZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KPiAtICogIGFsb25nIHdpdGggdGhpcyBwcm9n
cmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlDQo+IC0gKiAgRm91bmRhdGlv
biwgSW5jLiwgNTkgVGVtcGxlIFBsYWNlLCBTdWl0ZSAzMzAsIEJvc3RvbiwgTUEgIDAyMTExLTEz
MDcgIFVTQQ0KPiAtICoNCj4gICovDQo+IA0KPiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPg0K
PiArDQo+ICNpbmNsdWRlIDxsaW51eC9rZXJuZWwuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9pbml0
Lmg+DQo+ICsjaW5jbHVkZSA8bGludXgvdHlwZXMuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9mY250
bC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2ludGVycnVwdC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4
L3B0cmFjZS5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L3BvbGwuaD4NCj4gKw0KPiArI2luY2x1ZGUg
PGxpbnV4L3NsYWIuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC90dHkuaD4NCj4gI2luY2x1ZGUgPGxp
bnV4L2Vycm5vLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvc3RyaW5nLmg+DQo+ICsjaW5jbHVkZSA8
bGludXgvc2lnbmFsLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvaW9jdGwuaD4NCj4gI2luY2x1ZGUg
PGxpbnV4L3NrYnVmZi5oPg0KPiANCj4gI2luY2x1ZGUgPG5ldC9ibHVldG9vdGgvYmx1ZXRvb3Ro
Lmg+DQo+ICNpbmNsdWRlIDxuZXQvYmx1ZXRvb3RoL2hjaV9jb3JlLmg+DQo+IA0KPiAtI2luY2x1
ZGUgImJ0YmNtLmgiDQo+ICsjaW5jbHVkZSA8bGludXgvZ3Bpby9jb25zdW1lci5oPg0KPiArI2lu
Y2x1ZGUgPGxpbnV4L29mX2dwaW8uaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9vZl9wbGF0Zm9ybS5o
Pg0KPiArDQo+ICNpbmNsdWRlICJoY2lfdWFydC5oIg0KPiArI2luY2x1ZGUgImJ0YmNtLmgiDQo+
ICsjaW5jbHVkZSAiYnRiY21fdWFydC5oIg0KPiANCj4gLXN0cnVjdCBiY21fZGF0YSB7DQo+IC0J
c3RydWN0IHNrX2J1ZmYgKnJ4X3NrYjsNCj4gKy8qIFByb3RvY29sIGNvbnRleHQgKi8NCj4gK3N0
cnVjdCBiY21faDRfc3RydWN0IHsNCg0KDQpJIGFtIG1pc3NpbmcgdGhlIHJlYXNvbiBmb3IgdGhp
cyByZW5hbWluZy4NCg0KSUY6IEkndmUgYWN0dWFsbHkgc3RhcnRlZCB0aGlzIGRldmVsb3BtZW50
IGJlZm9yZSB5b3UndmUgY3JlYXRlZCB0aGlzIGZpbGUuIEhhdmUgbm93IHJlc3RvcmVkIHlvdXIg
bmFtZS4NCg0KPiAJc3RydWN0IHNrX2J1ZmZfaGVhZCB0eHE7DQo+ICsJc3RydWN0IGhjaV91YXJ0
ICpodTsNCj4gKw0KPiArCWJvb2wgaXNfc3VzcGVuZGVkOyAvKiBzdXNwZW5kL3Jlc3VtZSBmbGFn
ICovDQo+ICsNCj4gKwkvKiBSZWN2IGRhdGEgcGFyc2luZyBpcyBub3QgdXNlZCBpbiBub3JtYWwg
b3BlcmF0aW9uICovDQo+ICsJYm9vbCBwYXJzZV9yZWN2Ow0KPiArCS8qIGJ1ZmZlciBpbmNsdWRl
cyB0aGUgdHlwZSAqLw0KPiArCXVuc2lnbmVkIGNoYXIgcmVhc3NlbWJseVsxICsgSENJX01BWF9G
UkFNRV9TSVpFXTsNCj4gKwl1bnNpZ25lZCBpbnQgcnNpemU7DQo+ICsJdTE2IHJleHBlY3RlZDsN
Cg0KU28gdGhlIG5pY2UgSDo0IFJYIGhlbHBlciBmdW5jdGlvbiB0aGF0IEkgYnVpbGQgaXMgbm90
IGdvb2QgZW5vdWdoPyBSZWFsbHk/IFlvdSBuZWVkIHRvIHJlYWxseSBtYWtlIGEgZ29vZCBjYXNl
IGZvciBub3QgdXNpbmcgaXQuDQoNCklGOiBSZW1vdmVkIGl0Lg0KDQo+ICsNCj4gKwlzdHJ1Y3Qg
dGltZXJfbGlzdCB0aW1lcjsgLyogaWRsZSB0aW1lciAqLw0KPiArDQo+ICsJc3RydWN0IGJ0YmNt
X3VhcnRfcGFyYW1ldGVycyBwYXJzOyAvKiBkZXZpY2UgcGFyYW1ldGVycyAqLw0KPiArCXZvaWQg
KmRldmljZV9jb250ZXh0OyAvKiBBQ1BJL0RUIGRldmljZSBjb250ZXh0ICovDQo+IH07DQo+IA0K
PiAtc3RhdGljIGludCBiY21fb3BlbihzdHJ1Y3QgaGNpX3VhcnQgKmh1KQ0KPiArLyogU3RhdGlj
IGZ1bmN0aW9uIHByb3RvdHlwZXMgZm9yIGZvcndhcmQgcmVmZXJlbmNlcyAqLw0KPiArc3RhdGlj
IHZvaWQgc3VzcGVuZF9ub3RpZmljYXRpb24odm9pZCAqY29udGV4dCk7DQo+ICtzdGF0aWMgdm9p
ZCByZXN1bWVfbm90aWZpY2F0aW9uKHZvaWQgKmNvbnRleHQpOw0KPiArc3RhdGljIHZvaWQgd2Fr
ZXVwX25vdGlmaWNhdGlvbih2b2lkICpjb250ZXh0KTsNCj4gK3N0YXRpYyB2b2lkIGJjbV9lbnN1
cmVfd2FrZXVwKHN0cnVjdCBoY2lfdWFydCAqaHUpOw0KDQpJIGRpc2xpa2UgZm9yd2FyZCBkZWNs
YXJhdGlvbi4gSWYgdGhleSBjYW4gYmUgYXZvaWQsIGxldHMgYXZvaWQgdGhlbSBhbmQgdGhlIGZ1
bmN0aW9uIGludG8gdGhlIGJldHRlciBsb2NhdGlvbi4NCg0KSUY6IEZ1bmN0aW9ucyBtb3ZlZC4N
Cg0KPiArDQo+ICsvKiBTdXNwZW5kL3Jlc3VtZSBzeW5jaHJvbml6YXRpb24gbXV0ZXggKi8NCj4g
K3N0YXRpYyBERUZJTkVfTVVURVgocGxvY2spOw0KPiArDQo+ICsvKg0KPiArICogSWRsZSB0aW1l
ciBjYWxsYmFjaw0KPiArICovDQo+ICtzdGF0aWMgdm9pZCBiY21faWRsZV90aW1lb3V0KHVuc2ln
bmVkIGxvbmcgYXJnKQ0KPiB7DQo+IC0Jc3RydWN0IGJjbV9kYXRhICpiY207DQo+ICsJc3RydWN0
IGhjaV91YXJ0ICpodSA9IChzdHJ1Y3QgaGNpX3VhcnQgKilhcmc7DQo+ICsJc3RydWN0IGJjbV9o
NF9zdHJ1Y3QgKmg0ID0gaHUtPnByaXY7DQo+ICsJaW50IHN0YXR1czsNCj4gDQo+IC0JQlRfREJH
KCJodSAlcCIsIGh1KTsNCj4gKwlCVF9EQkcoImJjbV9pZGxlX3RpbWVvdXQgaHUgJXAiLCBodSk7
DQo+ICsNCj4gKwkvKiBTdXNwZW5kL3Jlc3VtZSBvcGVyYXRpb25zIGFyZSBzZXJpYWxpemVkICov
DQo+ICsJbXV0ZXhfbG9jaygmcGxvY2spOw0KPiArDQo+ICsJaWYgKCFoNC0+aXNfc3VzcGVuZGVk
KSB7DQo+ICsJCS8qIEZsb3cgY29udHJvbCB0aGUgcG9ydCBpZiBjb25maWd1cmVkICovDQo+ICsJ
CXN1c3BlbmRfbm90aWZpY2F0aW9uKGh1KTsNCj4gKw0KPiArCQkvKiBTdXNwZW5kIHRoZSBkZXZp
Y2UgKi8NCj4gKwkJc3RhdHVzID0gYnRiY21fdWFydF9jb250cm9sKEJUQkNNX1VBUlRfQUNUSU9O
X1NVU1BFTkQsDQo+ICsJCQkJCSAgICBoNC0+ZGV2aWNlX2NvbnRleHQsIE5VTEwsIE5VTEwpOw0K
PiArCQlpZiAoc3RhdHVzKQ0KPiArCQkJQlRfREJHKCJiY21faWRsZV90aW1lb3V0IGZhaWxlZCB0
byBzdXNwZW5kIGRldmljZSAlZCIsDQo+ICsJCQkgICAgICAgc3RhdHVzKTsNCj4gKwl9DQo+IA0K
PiAtCWJjbSA9IGt6YWxsb2Moc2l6ZW9mKCpiY20pLCBHRlBfS0VSTkVMKTsNCj4gLQlpZiAoIWJj
bSkNCj4gKwltdXRleF91bmxvY2soJnBsb2NrKTsNCj4gK30NCj4gKw0KPiArLyoNCj4gKyAqIElu
aXRpYWxpemUgcHJvdG9jb2wNCj4gKyAqLw0KPiArc3RhdGljIGludCBiY21faDRfb3BlbihzdHJ1
Y3QgaGNpX3VhcnQgKmh1KQ0KPiArew0KPiArCXN0cnVjdCBidGJjbV91YXJ0X2NhbGxiYWNrcyBj
YWxsYmFja3M7DQo+ICsJdW5zaWduZWQgbG9uZyBjYWxsYmFja3Nfc2l6ZSA9IHNpemVvZihjYWxs
YmFja3MpOw0KPiArCWludCBzdGF0dXM7DQo+ICsJc3RydWN0IGJjbV9oNF9zdHJ1Y3QgKmg0Ow0K
PiArCXN0cnVjdCB0dHlfc3RydWN0ICp0dHkgPSBodS0+dHR5Ow0KPiArDQo+ICsJQlRfREJHKCJi
Y21faDRfb3BlbiBodSAlcCIsIGh1KTsNCj4gKw0KPiArCWg0ID0ga3phbGxvYyhzaXplb2YoKmg0
KSwgR0ZQX0tFUk5FTCk7DQo+ICsJaWYgKCFoNCkNCj4gCQlyZXR1cm4gLUVOT01FTTsNCj4gDQo+
IC0Jc2tiX3F1ZXVlX2hlYWRfaW5pdCgmYmNtLT50eHEpOw0KPiArCXNrYl9xdWV1ZV9oZWFkX2lu
aXQoJmg0LT50eHEpOw0KPiArCWh1LT5wcml2ID0gaDQ7DQo+ICsJaDQtPmh1ID0gaHU7DQo+ICsJ
aDQtPmlzX3N1c3BlbmRlZCA9IGZhbHNlOw0KPiArCWg0LT5wYXJzZV9yZWN2ID0gZmFsc2U7DQo+
ICsJaDQtPnJzaXplID0gMDsNCj4gKw0KPiArCS8qIENvbmZpZ3VyZSBjYWxsYmFja3Mgb24gdGhl
IGRyaXZlciAqLw0KPiArCWNhbGxiYWNrcy5pbnRlcmZhY2VfdmVyc2lvbiA9IEJUQkNNX1VBUlRf
SU5URVJGQUNFX1ZFUlNJT047DQo+ICsJY2FsbGJhY2tzLmNvbnRleHQgPSBodTsNCj4gKwlzdHJj
cHkoY2FsbGJhY2tzLm5hbWUsIHR0eS0+bmFtZSk7DQo+ICsJY2FsbGJhY2tzLnBfc3VzcGVuZCA9
IHN1c3BlbmRfbm90aWZpY2F0aW9uOw0KPiArCWNhbGxiYWNrcy5wX3Jlc3VtZSA9IHJlc3VtZV9u
b3RpZmljYXRpb247DQo+ICsJY2FsbGJhY2tzLnBfd2FrZXVwID0gd2FrZXVwX25vdGlmaWNhdGlv
bjsNCj4gKwlzdGF0dXMgPSBidGJjbV91YXJ0X2NvbnRyb2woQlRCQ01fVUFSVF9BQ1RJT05fQ09O
RklHVVJFX0NBTExCQUNLUywNCj4gKwkJCQkgICAgTlVMTCwgJmNhbGxiYWNrcywgJmNhbGxiYWNr
c19zaXplKTsNCj4gKwlpZiAoc3RhdHVzKSB7DQo+ICsJCUJUX0RCRygiYmNtX2g0X29wZW4gZmFp
bGVkIHRvIHNldCBkcml2ZXIgY2FsbGJhY2tzICVkIiwgc3RhdHVzKTsNCj4gKwkJcmV0dXJuIHN0
YXR1czsNCj4gKwl9DQo+ICsJaWYgKGNhbGxiYWNrc19zaXplICE9IHNpemVvZih2b2lkICopKSB7
DQo+ICsJCUJUX0RCRygiYmNtX2g0X29wZW4gZ290IGJhY2sgJWQgYnl0ZXMgZnJvbSBjYWxsYmFj
a3M/ISIsDQo+ICsJCSAgICAgICAoaW50KWNhbGxiYWNrc19zaXplKTsNCj4gKwkJcmV0dXJuIC1F
TVNHU0laRTsNCj4gKwl9DQo+ICsJbWVtY3B5KCZoNC0+ZGV2aWNlX2NvbnRleHQsICZjYWxsYmFj
a3MsIHNpemVvZih2b2lkICopKTsNCj4gKwlCVF9EQkcoImJjbV9oNF9vcGVuIGNhbGxiYWNrcyBj
b250ZXh0ICVwIiwgaDQtPmRldmljZV9jb250ZXh0KTsNCj4gKw0KPiArCS8qIFJldHJpZXZlIGRl
dmljZSBwYXJhbWV0ZXJzICovDQo+ICsJY2FsbGJhY2tzX3NpemUgPSBzaXplb2YoaDQtPnBhcnMp
Ow0KPiArCXN0YXR1cyA9IGJ0YmNtX3VhcnRfY29udHJvbChCVEJDTV9VQVJUX0FDVElPTl9HRVRf
UEFSQU1FVEVSUywNCj4gKwkJCQkgICAgaDQtPmRldmljZV9jb250ZXh0LCAmaDQtPnBhcnMsDQo+
ICsJCQkJICAgICZjYWxsYmFja3Nfc2l6ZSk7DQo+ICsJaWYgKHN0YXR1cykgew0KPiArCQlCVF9E
QkcoImJjbV9oNF9vcGVuIGZhaWxlZCB0byBnZXQgZGV2IHBhcmFtZXRlcnMgJWQiLCBzdGF0dXMp
Ow0KPiArCQlyZXR1cm4gc3RhdHVzOw0KPiArCX0NCj4gKwlCVF9EQkcoIlBhcnMgdmVyICVkIGNz
bGVlcCAlZCBkYWxvdyAlZCBiYWxvdyAlZCBpZGxlICVkIiwNCj4gKwkgICAgICAgaDQtPnBhcnMu
aW50ZXJmYWNlX3ZlcnNpb24sIGg0LT5wYXJzLmNvbmZpZ3VyZV9zbGVlcCwNCj4gKwkgICAgICAg
aDQtPnBhcnMuZGV2X3dha2VfYWN0aXZlX2xvdywgaDQtPnBhcnMuYnRfd2FrZV9hY3RpdmVfbG93
LA0KPiArCSAgICAgICBoNC0+cGFycy5pZGxlX3RpbWVvdXRfaW5fc2Vjcyk7DQo+ICsNCj4gKwkv
KiBDeWNsZSBwb3dlciB0byBtYWtlIHN1cmUgdGhlIGRldmljZSBpcyBpbiB0aGUga25vd24gc3Rh
dGUgKi8NCj4gKwlzdGF0dXMgPSBidGJjbV91YXJ0X2NvbnRyb2woQlRCQ01fVUFSVF9BQ1RJT05f
UE9XRVJfT0ZGLA0KPiArCQkJCSAgICBoNC0+ZGV2aWNlX2NvbnRleHQsIE5VTEwsIE5VTEwpOw0K
PiArCWlmIChzdGF0dXMpIHsNCj4gKwkJQlRfREJHKCJiY21faDRfb3BlbiBmYWlsZWQgdG8gcG93
ZXIgb2ZmICVkIiwgc3RhdHVzKTsNCj4gKwl9IGVsc2Ugew0KPiArCQlzdGF0dXMgPSBidGJjbV91
YXJ0X2NvbnRyb2woQlRCQ01fVUFSVF9BQ1RJT05fUE9XRVJfT04sDQo+ICsJCQkJCSAgICBoNC0+
ZGV2aWNlX2NvbnRleHQsIE5VTEwsIE5VTEwpOw0KPiArCQlpZiAoc3RhdHVzKQ0KPiArCQkJQlRf
REJHKCJiY21faDRfb3BlbiBmYWlsZWQgdG8gcG93ZXIgb24gJWQiLCBzdGF0dXMpOw0KPiArCX0N
Cj4gKw0KPiArCS8qIFN0YXJ0IHRoZSBpZGxlIHRpbWVyICovDQo+ICsJaWYgKGg0LT5wYXJzLmNv
bmZpZ3VyZV9zbGVlcCkgew0KPiArCQlzZXR1cF90aW1lcigmaDQtPnRpbWVyLCBiY21faWRsZV90
aW1lb3V0LCAodW5zaWduZWQgbG9uZylodSk7DQo+ICsJCWlmIChoNC0+cGFycy5jb25maWd1cmVf
c2xlZXApDQo+ICsJCQltb2RfdGltZXIoJmg0LT50aW1lciwgamlmZmllcyArIG1zZWNzX3RvX2pp
ZmZpZXMoDQo+ICsJCQkJICBoNC0+cGFycy5pZGxlX3RpbWVvdXRfaW5fc2VjcyAqIDEwMDApKTsN
Cj4gKwl9DQo+ICsNCj4gKwlyZXR1cm4gMDsNCj4gK30NCj4gKw0KPiArLyoNCj4gKyAqIEZsdXNo
IHByb3RvY29sIGRhdGENCj4gKyAqLw0KPiArc3RhdGljIGludCBiY21faDRfZmx1c2goc3RydWN0
IGhjaV91YXJ0ICpodSkNCj4gK3sNCj4gKwlzdHJ1Y3QgYmNtX2g0X3N0cnVjdCAqaDQgPSBodS0+
cHJpdjsNCj4gDQo+IC0JaHUtPnByaXYgPSBiY207DQo+ICsJQlRfREJHKCJiY21faDRfZmx1c2gg
aHUgJXAiLCBodSk7DQo+ICsNCj4gKwlza2JfcXVldWVfcHVyZ2UoJmg0LT50eHEpOw0KPiAJcmV0
dXJuIDA7DQo+IH0NCj4gDQo+IC1zdGF0aWMgaW50IGJjbV9jbG9zZShzdHJ1Y3QgaGNpX3VhcnQg
Kmh1KQ0KPiArLyoNCj4gKyAqIE1ha2Ugc3VyZSB3ZSdyZSBhd2FrZQ0KPiArICogKGNhbGxlZCB3
aGVuIHRoZSByZXN1bWVkIHN0YXRlIGlzIHJlcXVpcmVkKQ0KPiArICovDQo+ICtzdGF0aWMgdm9p
ZCBiY21fZW5zdXJlX3dha2V1cChzdHJ1Y3QgaGNpX3VhcnQgKmh1KQ0KPiB7DQo+IC0Jc3RydWN0
IGJjbV9kYXRhICpiY20gPSBodS0+cHJpdjsNCj4gKwlzdHJ1Y3QgYmNtX2g0X3N0cnVjdCAqaDQg
PSBodS0+cHJpdjsNCj4gKwlpbnQgc3RhdHVzOw0KPiArDQo+ICsJaWYgKCFoNC0+cGFycy5jb25m
aWd1cmVfc2xlZXApDQo+ICsJCXJldHVybjsNCj4gKw0KPiArCS8qIFN1c3BlbmQvcmVzdW1lIG9w
ZXJhdGlvbnMgYXJlIHNlcmlhbGl6ZWQgKi8NCj4gKwltdXRleF9sb2NrKCZwbG9jayk7DQo+ICsN
Cj4gKwkvKiBOb3RoaW5nIHRvIGRvIGlmIHJlc3VtZWQgYWxyZWFkeSAqLw0KPiArCWlmICghaDQt
PmlzX3N1c3BlbmRlZCkgew0KPiArCQltdXRleF91bmxvY2soJnBsb2NrKTsNCj4gDQo+IC0JQlRf
REJHKCJodSAlcCIsIGh1KTsNCj4gKwkJLyogSnVzdCByZXNldCB0aGUgdGltZXIgKi8NCj4gKwkJ
c3RhdHVzID0gbW9kX3RpbWVyKCZoNC0+dGltZXIsIGppZmZpZXMgKyBtc2Vjc190b19qaWZmaWVz
KA0KPiArCQkJCSAgIGg0LT5wYXJzLmlkbGVfdGltZW91dF9pbl9zZWNzICogMTAwMCkpOw0KPiAr
CQlyZXR1cm47DQo+ICsJfQ0KDQpVc2luZyBhIHRpbWVyIGlzIHByZXR0eSBkYW5nZXJvdXMuIFdl
IGFyZSB0cnlpbmcgdG8gZ2V0IGF3YXkgZnJvbSB0aGVtIGFzIG11Y2ggYXMgcG9zc2libGUgdW5s
ZXNzIHdlIHJlYWxseSBuZWVkIHRoZW0uIENhbiB3ZSBqdXN0IHVzZSBhIGRlbGF5ZWQgd29yayBz
dHJ1Y3QgaGVyZT8NCg0KSUY6IFRoZSBnb2FsIGhlcmUgaXMgdG8gdHJpZ2dlciBjZXJ0YWluIGFj
dGlvbnMgd2hlbiB0aGVyZSBoYXMgYmVlbiB0aGF0IG1hbnkgc2Vjb25kcyB3aXRoIG5vIGFjdGl2
aXR5LiBJIGJlbGlldmUgdGhhdCwgc2hvcnQgb2YgYSBkZWRpY2F0ZWQgdGhyZWFkIG9yIGEgbmV2
ZXIgZW5kaW5nIHdvcmsgcXVldWUsIHRpbWVyIGlzIHRoZSBiZXN0IG1lY2hhbmlzbSB0byBpbXBs
ZW1lbnQgdGhhdC4gVGltZXIgaXMgY2xlYXJseSBjaGVhcGVyIHRoYW4gYSB0aHJlYWQuDQoNCj4g
Kw0KPiArCS8qIFdha2V1cCB0aGUgZGV2aWNlICovDQo+ICsJc3RhdHVzID0gYnRiY21fdWFydF9j
b250cm9sKEJUQkNNX1VBUlRfQUNUSU9OX1JFU1VNRSwNCj4gKwkJCQkgICAgaDQtPmRldmljZV9j
b250ZXh0LCBOVUxMLCBOVUxMKTsNCj4gKwlpZiAoc3RhdHVzKQ0KPiArCQlCVF9EQkcoImJjbV9l
bnN1cmVfd2FrZXVwIGZhaWxlZCB0byByZXN1bWUgZHJpdmVyICVkIiwgc3RhdHVzKTsNCj4gDQo+
IC0Jc2tiX3F1ZXVlX3B1cmdlKCZiY20tPnR4cSk7DQo+IC0Ja2ZyZWVfc2tiKGJjbS0+cnhfc2ti
KTsNCj4gLQlrZnJlZShiY20pOw0KPiArCS8qIFVuZmxvdyBjb250cm9sIHRoZSBwb3J0IGlmIGNv
bmZpZ3VyZWQgKi8NCj4gKwlyZXN1bWVfbm90aWZpY2F0aW9uKGh1KTsNCj4gKw0KPiArCW11dGV4
X3VubG9jaygmcGxvY2spOw0KPiArfQ0KPiArDQo+ICsvKg0KPiArICogQ2xvc2UgcHJvdG9jb2wN
Cj4gKyAqLw0KPiArc3RhdGljIGludCBiY21faDRfY2xvc2Uoc3RydWN0IGhjaV91YXJ0ICpodSkN
Cj4gK3sNCj4gKwlzdHJ1Y3QgYnRiY21fdWFydF9jYWxsYmFja3MgY2FsbGJhY2tzOw0KPiArCXVu
c2lnbmVkIGxvbmcgY2FsbGJhY2tzX3NpemUgPSBzaXplb2YoY2FsbGJhY2tzKTsNCj4gKwlzdHJ1
Y3QgYmNtX2g0X3N0cnVjdCAqaDQgPSBodS0+cHJpdjsNCj4gKwlpbnQgc3RhdHVzOw0KPiANCj4g
CWh1LT5wcml2ID0gTlVMTDsNCj4gKw0KPiArCUJUX0RCRygiYmNtX2g0X2Nsb3NlIGh1ICVwIiwg
aHUpOw0KPiArDQo+ICsJLyogSWYgd2UncmUgYmVpbmcgY2xvc2VkLCB3ZSBtdXN0IHN1c3BlbmQg
Ki8NCj4gKwlpZiAoaDQtPnBhcnMuY29uZmlndXJlX3NsZWVwKSB7DQo+ICsJCW11dGV4X2xvY2so
JnBsb2NrKTsNCj4gKw0KPiArCQlpZiAoIWg0LT5pc19zdXNwZW5kZWQpIHsNCj4gKwkJCS8qIEZs
b3cgY29udHJvbCB0aGUgcG9ydCAqLw0KPiArCQkJc3VzcGVuZF9ub3RpZmljYXRpb24oaHUpOw0K
PiArDQo+ICsJCQkvKiBTdXNwZW5kIHRoZSBkZXZpY2UgKi8NCj4gKwkJCXN0YXR1cyA9IGJ0YmNt
X3VhcnRfY29udHJvbChCVEJDTV9VQVJUX0FDVElPTl9TVVNQRU5ELA0KPiArCQkJCQkJICAgIGg0
LT5kZXZpY2VfY29udGV4dCwgTlVMTCwNCj4gKwkJCQkJCSAgICBOVUxMKTsNCj4gKwkJCWlmIChz
dGF0dXMpIHsNCj4gKwkJCQlCVF9EQkcoImJjbV9oNF9jbG9zZSBzdXNwZW5kIGRyaXZlciBmYWls
ICVkIiwNCj4gKwkJCQkgICAgICAgc3RhdHVzKTsNCj4gKwkJCX0NCj4gKwkJfQ0KPiArDQo+ICsJ
bXV0ZXhfdW5sb2NrKCZwbG9jayk7DQo+ICsNCj4gKwlkZWxfdGltZXJfc3luYygmaDQtPnRpbWVy
KTsNCj4gKwl9DQo+ICsNCj4gKwkvKiBQb3dlciBvZmYgdGhlIGRldmljZSBpZiBwb3NzaWJsZSAq
Lw0KPiArCXN0YXR1cyA9IGJ0YmNtX3VhcnRfY29udHJvbChCVEJDTV9VQVJUX0FDVElPTl9QT1dF
Ul9PRkYsDQo+ICsJCQkJICAgIGg0LT5kZXZpY2VfY29udGV4dCwgTlVMTCwgTlVMTCk7DQo+ICsJ
aWYgKHN0YXR1cykNCj4gKwkJQlRfREJHKCJiY21faDRfY2xvc2UgZmFpbGVkIHRvIHBvd2VyIG9m
ZiAlZCIsIHN0YXR1cyk7DQo+ICsNCj4gKwkvKiBkZS1jb25maWd1cmUgY2FsbGJhY2tzIG9uIHRo
ZSBkcml2ZXIgKi8NCj4gKwljYWxsYmFja3MuaW50ZXJmYWNlX3ZlcnNpb24gPSBCVEJDTV9VQVJU
X0lOVEVSRkFDRV9WRVJTSU9OOw0KPiArCWNhbGxiYWNrcy5jb250ZXh0ID0gaHU7DQo+ICsJY2Fs
bGJhY2tzLnBfc3VzcGVuZCA9IE5VTEw7DQo+ICsJY2FsbGJhY2tzLnBfcmVzdW1lID0gTlVMTDsN
Cj4gKwljYWxsYmFja3MucF93YWtldXAgPSBOVUxMOw0KPiArCXN0YXR1cyA9IGJ0YmNtX3VhcnRf
Y29udHJvbChCVEJDTV9VQVJUX0FDVElPTl9DT05GSUdVUkVfQ0FMTEJBQ0tTLA0KPiArCQkJCSAg
ICBoNC0+ZGV2aWNlX2NvbnRleHQsICZjYWxsYmFja3MsDQo+ICsJCQkJICAgICZjYWxsYmFja3Nf
c2l6ZSk7DQo+ICsJaWYgKHN0YXR1cykNCj4gKwkJQlRfREJHKCJiY21faDRfY2xvc2UgZmFpbGVk
IHRvIHJlc2V0IGRydiBjYWxsYmFja3MgJWQiLCBzdGF0dXMpOw0KPiArCXNrYl9xdWV1ZV9wdXJn
ZSgmaDQtPnR4cSk7DQo+ICsNCj4gKwlodS0+cHJpdiA9IE5VTEw7DQo+ICsJa2ZyZWUoaDQpOw0K
PiArDQo+IAlyZXR1cm4gMDsNCj4gfQ0KPiANCj4gLXN0YXRpYyBpbnQgYmNtX2ZsdXNoKHN0cnVj
dCBoY2lfdWFydCAqaHUpDQo+ICsvKg0KPiArICogRW5xdWV1ZSBmcmFtZSBmb3IgdHJhbnNtaXR0
aW9uIChwYWRkaW5nLCBjcmMsIGV0YykNCj4gKyAqLw0KPiArc3RhdGljIGludCBiY21faDRfZW5x
dWV1ZShzdHJ1Y3QgaGNpX3VhcnQgKmh1LCBzdHJ1Y3Qgc2tfYnVmZiAqc2tiKQ0KPiB7DQo+IC0J
c3RydWN0IGJjbV9kYXRhICpiY20gPSBodS0+cHJpdjsNCj4gKwlzdHJ1Y3QgYmNtX2g0X3N0cnVj
dCAqaDQgPSBodS0+cHJpdjsNCj4gKw0KPiArCUJUX0RCRygiYmNtX2g0X2VucXVldWUgaHUgJXAg
c2tiICVwIHR5cGUgJWQgbGVuICVkICgleCAleCAleCkiLA0KPiArCSAgICAgICBodSwgc2tiLCBi
dF9jYihza2IpLT5wa3RfdHlwZSwgc2tiLT5sZW4sDQo+ICsJCXNrYi0+ZGF0YVswXSwgc2tiLT5k
YXRhWzFdLCBza2ItPmRhdGFbMl0pOw0KPiANCj4gLQlCVF9EQkcoImh1ICVwIiwgaHUpOw0KPiAr
CS8qIE1ha2Ugc3VyZSB3ZSdyZSByZXN1bWVkICovDQo+ICsJYmNtX2Vuc3VyZV93YWtldXAoaHUp
Ow0KPiANCj4gLQlza2JfcXVldWVfcHVyZ2UoJmJjbS0+dHhxKTsNCj4gKwkvKiBQcmVwZW5kIHNr
YiB3aXRoIGZyYW1lIHR5cGUgKi8NCj4gKwltZW1jcHkoc2tiX3B1c2goc2tiLCAxKSwgJmJ0X2Ni
KHNrYiktPnBrdF90eXBlLCAxKTsNCj4gKwlza2JfcXVldWVfdGFpbCgmaDQtPnR4cSwgc2tiKTsN
Cj4gDQo+IAlyZXR1cm4gMDsNCj4gfQ0KPiANCj4gLXN0YXRpYyBpbnQgYmNtX3NldHVwKHN0cnVj
dCBoY2lfdWFydCAqaHUpDQo+ICsvKg0KPiArICogSENJIGV2ZW50IHByb2Nlc3NpbmcgaWYgb3Vy
IG93biBwYXJzaW5nIG9mIGV2ZW50cyBpcyByZXF1aXJlZA0KPiArICogTk9URTogcmV0dXJuIDAg
aWYgdGhlIGV2ZW50IGlzIG5vdCB0byBiZSBwYXNzZWQgdXAgdG8gQmx1ZVoNCj4gKyAqLw0KPiAr
c3RhdGljIGJvb2wgYmNtX3Byb2Nlc3NfaGNpX2V2ZW50KHN0cnVjdCBoY2lfdWFydCAqaHUpDQo+
ICt7DQo+ICsJc3RydWN0IGJjbV9oNF9zdHJ1Y3QgKmg0ID0gaHUtPnByaXY7DQo+ICsJaW50IHN0
YXR1czsNCj4gKwl1bnNpZ25lZCBjaGFyIGV2dF9jb2RlID0gaDQtPnJlYXNzZW1ibHlbMV07DQo+
ICsJdW5zaWduZWQgY2hhciBsZW4gPSBoNC0+cmVhc3NlbWJseVsyXTsNCj4gKw0KPiArCUJUX0RC
RygiYmNtX3Byb2Nlc3NfaGNpX2V2ZW50ICUwMnggZXZlbnQgJTAyeCBsZW4gJTAyeCAlMDJ4IiwN
Cj4gKwkgICAgICAgaDQtPnJlYXNzZW1ibHlbMF0sIGV2dF9jb2RlLCBsZW4sIGg0LT5yZWFzc2Vt
Ymx5WzNdKTsNCj4gKw0KPiArCS8qIHN3aXRjaCAoZXZ0X2NvZGUpIHsgY2FzZSBIQ0lfRVZfQ01E
X0NPTVBMRVRFOiBicmVhayB9ICovDQo+ICsNCj4gKwlzdGF0dXMgPSBoY2lfcmVjdl9zdHJlYW1f
ZnJhZ21lbnQoaHUtPmhkZXYsIGg0LT5yZWFzc2VtYmx5LCBoNC0+cnNpemUpOw0KPiArCWlmIChz
dGF0dXMgPCAwKQ0KPiArCQlCVF9FUlIoImJjbV9wcm9jZXNzX2hjaV9ldmVudCAtIHJlYXNzZW1i
bHkgZmFpbGVkICVkIiwgc3RhdHVzKTsNCj4gKwlyZXR1cm4gdHJ1ZTsNCj4gK30NCj4gKw0KPiAr
LyoNCj4gKyAqIEFDTCBkYXRhIHByb2Nlc3NpbmcgaWYgb3VyIG93biBwYXJzaW5nIG9mIGRhdGEg
aXMgcmVxdWlyZWQNCj4gKyAqIE5PVEU6IHJldHVybiAwIGlmIHRoZSBkYXRhIGlzIG5vdCB0byBi
ZSBwYXNzZWQgdXAgdG8gQmx1ZVoNCj4gKyAqLw0KPiArc3RhdGljIGJvb2wgYmNtX3Byb2Nlc3Nf
YWNsX2RhdGEoc3RydWN0IGhjaV91YXJ0ICpodSkNCj4gew0KPiAtCUJUX0RCRygiaHUgJXAiLCBo
dSk7DQo+ICsJc3RydWN0IGJjbV9oNF9zdHJ1Y3QgKmg0ID0gaHUtPnByaXY7DQo+ICsJaW50IHJl
dDsNCj4gDQo+IC0JaHUtPmhkZXYtPnNldF9iZGFkZHIgPSBidGJjbV9zZXRfYmRhZGRyOw0KPiAr
CUJUX0RCRygiYmNtX3Byb2Nlc3NfYWNsX2RhdGEgJTAyeCAlMDJ4ICUwMnggJTAyeCAlMDJ4IiwN
Cj4gKwkgICAgICAgaDQtPnJlYXNzZW1ibHlbMF0sIGg0LT5yZWFzc2VtYmx5WzFdLCBoNC0+cmVh
c3NlbWJseVsyXSwNCj4gKwkgICAgICAgaDQtPnJlYXNzZW1ibHlbM10sIGg0LT5yZWFzc2VtYmx5
WzRdKTsNCj4gDQo+IC0JcmV0dXJuIGJ0YmNtX3NldHVwX3BhdGNocmFtKGh1LT5oZGV2KTsNCj4g
KwlyZXQgPSBoY2lfcmVjdl9zdHJlYW1fZnJhZ21lbnQoaHUtPmhkZXYsIGg0LT5yZWFzc2VtYmx5
LCBoNC0+cnNpemUpOw0KPiArCWlmIChyZXQgPCAwKQ0KPiArCQlCVF9FUlIoImJjbV9wcm9jZXNz
X2FjbF9kYXRhIC0gRnJhbWUgUmVhc3NlbWJseSBGYWlsZWQiKTsNCj4gKwlyZXR1cm4gdHJ1ZTsN
Cj4gfQ0KPiANCj4gLXN0YXRpYyBjb25zdCBzdHJ1Y3QgaDRfcmVjdl9wa3QgYmNtX3JlY3ZfcGt0
c1tdID0gew0KPiAtCXsgSDRfUkVDVl9BQ0wsICAgLnJlY3YgPSBoY2lfcmVjdl9mcmFtZSB9LA0K
PiAtCXsgSDRfUkVDVl9TQ08sICAgLnJlY3YgPSBoY2lfcmVjdl9mcmFtZSB9LA0KPiAtCXsgSDRf
UkVDVl9FVkVOVCwgLnJlY3YgPSBoY2lfcmVjdl9mcmFtZSB9LA0KPiAtfTsNCj4gKy8qDQo+ICsg
KiBGcmFnbWVudCBwYXJzaW5nIGluIHRoZSBhY3RpdmUgZmlsdGVyaW5nIHBoYXNlDQo+ICsgKiAo
bm90IGN1cnJlbnRseSBhY3RpdmVseSB1c2VkKQ0KPiArICovDQo+ICtzdGF0aWMgdm9pZCBwYXJz
ZV9mcmFnbWVudChzdHJ1Y3QgaGNpX3VhcnQgKmh1LCB1bnNpZ25lZCBjaGFyICpkYXRhLCBpbnQg
Y291bnQpDQo+ICt7DQo+ICsJc3RydWN0IGJjbV9oNF9zdHJ1Y3QgKmg0ID0gaHUtPnByaXY7DQo+
ICsNCj4gKwlpZiAoaDQtPnJzaXplKQ0KPiArCQlCVF9EQkcoInBhcnNlX2ZyYWdtZW50IHR5cGUg
JXggZXhwZWN0ZWQgJWQiLA0KPiArCQkgICAgICAgaDQtPnJlYXNzZW1ibHlbMF0sIGg0LT5yZXhw
ZWN0ZWQpOw0KPiArDQo+ICsJd2hpbGUgKGNvdW50KSB7DQo+ICsJCWlmICghaDQtPnJzaXplKSB7
DQo+ICsJCQkvKiBTdGFydCBvZiB0aGUgZnJhbWUgKi8NCj4gKwkJCWg0LT5yZWFzc2VtYmx5WzBd
ID0gKmRhdGErKzsNCj4gKwkJCWg0LT5yc2l6ZSsrOw0KPiArCQkJY291bnQtLTsNCj4gKwkJCWNv
bnRpbnVlOw0KPiArCQl9DQo+ICsNCj4gKwkJc3dpdGNoIChoNC0+cmVhc3NlbWJseVswXSkgew0K
PiArCQljYXNlIEhDSV9FVkVOVF9QS1Q6DQo+ICsJCWlmIChoNC0+cnNpemUgPT0gMSkgew0KPiAr
CQkJLyogZXZlbnQgcHJvcGVyICovDQo+ICsJCQloNC0+cmVhc3NlbWJseVtoNC0+cnNpemUrK10g
PSAqZGF0YSsrOw0KPiArCQkJY291bnQtLTsNCj4gKwkJCWNvbnRpbnVlOw0KPiArCQl9DQo+ICsJ
CWlmIChoNC0+cnNpemUgPT0gMikgew0KPiArCQkJLyogbGVuZ3RoICovDQo+ICsJCQloNC0+cmV4
cGVjdGVkID0gKmRhdGE7DQo+ICsJCQloNC0+cmVhc3NlbWJseVtoNC0+cnNpemUrK10gPSAqZGF0
YSsrOw0KPiArCQkJY291bnQtLTsNCj4gKwkJCUJUX0RCRygiZXZ0aGRyIGxlbiAlZCwgbGVmdCAl
ZCIsIGg0LT5yZXhwZWN0ZWQsIGNvdW50KTsNCj4gKwkJCWNvbnRpbnVlOw0KPiArCQl9DQo+ICsJ
CWlmIChjb3VudCA+PSBoNC0+cmV4cGVjdGVkKSB7DQo+ICsJCQltZW1jcHkoJmg0LT5yZWFzc2Vt
Ymx5W2g0LT5yc2l6ZV0sIGRhdGEsIGg0LT5yZXhwZWN0ZWQpOw0KPiArCQkJaDQtPnJzaXplICs9
IGg0LT5yZXhwZWN0ZWQ7DQo+ICsJCQlkYXRhICs9IGg0LT5yZXhwZWN0ZWQ7DQo+ICsJCQljb3Vu
dCAtPSBoNC0+cmV4cGVjdGVkOw0KPiArCQkJYmNtX3Byb2Nlc3NfaGNpX2V2ZW50KGh1KTsNCj4g
KwkJCWg0LT5yc2l6ZSA9IDA7IC8qIHN0YXJ0aW5nIGFuZXcgKi8NCj4gKwkJCWNvbnRpbnVlOw0K
PiArCQl9DQo+ICsJCS8qIG9ubHkgcGllY2Ugb2YgdGhlIGV2ZW50IHJlY2VpdmVkICovDQo+ICsJ
CW1lbWNweSgmaDQtPnJlYXNzZW1ibHlbaDQtPnJzaXplXSwgZGF0YSwgY291bnQpOw0KPiArCQlo
NC0+cnNpemUgKz0gY291bnQ7DQo+ICsJCWRhdGEgKz0gY291bnQ7DQo+ICsJCWg0LT5yZXhwZWN0
ZWQgLT0gY291bnQ7DQo+ICsJCWNvdW50ID0gMDsNCj4gKwkJYnJlYWs7DQo+ICsNCj4gKwkJY2Fz
ZSBIQ0lfQUNMREFUQV9QS1Q6DQo+ICsJCWlmICgoaDQtPnJzaXplID09IDEpIHx8IChoNC0+cnNp
emUgPT0gMikgfHwgKGg0LT5yc2l6ZSA9PSAzKSkgew0KPiArCQkJLyogaGFuZGxlIGFuZCBmaXJz
dCBieXRlIG9mIGxlbmd0aCAqLw0KPiArCQkJaDQtPnJlYXNzZW1ibHlbaDQtPnJzaXplKytdID0g
KmRhdGErKzsNCj4gKwkJCWNvdW50LS07DQo+ICsJCQljb250aW51ZTsNCj4gKwkJfQ0KPiArCQlp
ZiAoaDQtPnJzaXplID09IDQpIHsNCj4gKwkJCS8qIGxhc3QgYnl0ZSBvZiB0aGUgbGVuZ3RoICov
DQo+ICsJCQloNC0+cmVhc3NlbWJseVtoNC0+cnNpemUrK10gPSAqZGF0YSsrOw0KPiArCQkJaDQt
PnJleHBlY3RlZCA9IGg0LT5yZWFzc2VtYmx5W2g0LT5yc2l6ZSAtIDJdICsNCj4gKwkJCQkoaDQt
PnJlYXNzZW1ibHlbaDQtPnJzaXplIC0gMV0gPDwgOCk7DQo+ICsJCQljb3VudC0tOw0KPiArCQkJ
QlRfREJHKCJkYXRoZHIgbGVuICVkLCBsZWZ0ICVkIiwgaDQtPnJleHBlY3RlZCwgY291bnQpOw0K
PiArCQkJY29udGludWU7DQo+ICsJCX0NCj4gKwkJaWYgKGNvdW50ID49IGg0LT5yZXhwZWN0ZWQp
IHsNCj4gKwkJCW1lbWNweSgmaDQtPnJlYXNzZW1ibHlbaDQtPnJzaXplXSwgZGF0YSwgaDQtPnJl
eHBlY3RlZCk7DQo+ICsJCQloNC0+cnNpemUgKz0gaDQtPnJleHBlY3RlZDsNCj4gKwkJCWRhdGEg
Kz0gaDQtPnJleHBlY3RlZDsNCj4gKwkJCWNvdW50IC09IGg0LT5yZXhwZWN0ZWQ7DQo+ICsJCQli
Y21fcHJvY2Vzc19hY2xfZGF0YShodSk7DQo+ICsJCQloNC0+cnNpemUgPSAwOyAvKiBzdGFydGlu
ZyBhbmV3ICovDQo+ICsJCQljb250aW51ZTsNCj4gKwkJfQ0KPiArCQkvKiBvbmx5IHBpZWNlIG9m
IGRhdGEgcmVjZWl2ZWQgKi8NCj4gKwkJbWVtY3B5KCZoNC0+cmVhc3NlbWJseVtoNC0+cnNpemVd
LCBkYXRhLCBjb3VudCk7DQo+ICsJCWg0LT5yc2l6ZSArPSBjb3VudDsNCj4gKwkJZGF0YSArPSBj
b3VudDsNCj4gKwkJaDQtPnJleHBlY3RlZCAtPSBjb3VudDsNCj4gKwkJY291bnQgPSAwOw0KPiAr
CQlicmVhazsNCj4gKw0KPiArCQlkZWZhdWx0OiAvKiBOb3RlIHRoYXQgU0NPIG1heSBOT1QgY29t
ZSB0aHJvdWdoIHRoZSBVQVJUICovDQoNCllvdSBjYW4gbm90IHJlYWxseSBtYWtlIHRoaXMgc3Rh
dGVtZW50LiBUaGlzIGhhcyB0byB3b3JrIGV2ZW4gd2l0aCBwbGF0Zm9ybSB3aGVyZSBTQ08gbWln
aHQgYWN0dWFsbHkgY29tZSB0aHJvdWdoIGFuIFVBUlQuIFRoZSBhdXRvbW90aXZlIHBsYXRmb3Jt
cyBoaXN0b3JpY2FsbHkgcHJlZmVycmVkIFNDTyBvdmVyIFVBUlQuDQoNClRoZW4gYWdhaW4sIEkg
YW0gbm90IGV2ZW4gcmV2aWV3aW5nIHRoaXMgcGllY2Ugb2YgY29kZS4gVXNlIHRoZSBoNF9yZWN2
X2J1ZiBoZWxwZXIuIEkgc2VuZCBhcm91bmQgZXhhbXBsZXMgZm9yIHVzaW5nIGl0IGV2ZW4gd2l0
aCB2ZW5kb3IgcGFja2V0cyBsaWtlIHdoYXQgTm9raWEgdXNlZCBpbiB0aGVpciBwbGF0Zm9ybXMu
DQoNCklGOiBTQ08gbWF5IG5vdCBjb21lIHRocm91Z2ggVUFSVCBpbiBCcm9hZGNvbSBkZXZpY2Vz
LiBJbiBhbnkgY2FzZSwgSSAndmUgcmVtb3ZlZCB0aGlzIGNvZGUuDQoNCj4gKwkJaWYgKGNvdW50
ID49IDMpDQo+ICsJCQlCVF9EQkcoInVuZXhwZWN0ZWQgcGt0IHR5cGUgb2YgJXggKCUwMnggJTAy
eCAlMDJ4KT8hIiwNCj4gKwkJCSAgICAgICBoNC0+cmVhc3NlbWJseVswXSwgZGF0YVswXSwgZGF0
YVsxXSwgZGF0YVsyXSk7DQo+ICsJCWVsc2UgaWYgKGNvdW50ID09IDIpDQo+ICsJCQlCVF9EQkco
InVuZXhwZWN0ZWQgcGt0IHR5cGUgb2YgJXggKCUwMnggJTAyeCk/ISIsDQo+ICsJCQkgICAgICAg
aDQtPnJlYXNzZW1ibHlbMF0sIGRhdGFbMF0sIGRhdGFbMV0pOw0KPiArCQllbHNlIGlmIChjb3Vu
dCA9PSAxKQ0KPiArCQkJQlRfREJHKCJ1bmV4cGVjdGVkIHBrdCB0eXBlIG9mICV4ICglMDJ4KT8h
IiwNCj4gKwkJCSAgICAgICBoNC0+cmVhc3NlbWJseVswXSwgZGF0YVswXSk7DQo+ICsJCWVsc2UN
Cj4gKwkJCUJUX0RCRygidW5leHBlY3RlZCBwa3QgdHlwZSBvZiAleD8hIiwNCj4gKwkJCSAgICAg
ICBoNC0+cmVhc3NlbWJseVswXSk7DQo+ICsJCWg0LT5yc2l6ZSA9IDA7DQo+ICsJCXJldHVybjsN
Cj4gKwl9DQo+ICsJfQ0KPiArfQ0KPiANCj4gLXN0YXRpYyBpbnQgYmNtX3JlY3Yoc3RydWN0IGhj
aV91YXJ0ICpodSwgY29uc3Qgdm9pZCAqZGF0YSwgaW50IGNvdW50KQ0KPiArLyoNCj4gKyAqIERh
dGEgaW5kaWNhdGlvbiBmcm9tIHRoZSBsaW5lIGRpc2NpcGxpbmUNCj4gKyAqLw0KPiArc3RhdGlj
IGludCBiY21faDRfcmVjdihzdHJ1Y3QgaGNpX3VhcnQgKmh1LCB2b2lkICpkYXRhLCBpbnQgY291
bnQpDQo+IHsNCj4gLQlzdHJ1Y3QgYmNtX2RhdGEgKmJjbSA9IGh1LT5wcml2Ow0KPiArCXN0cnVj
dCBiY21faDRfc3RydWN0ICpoNCA9IGh1LT5wcml2Ow0KPiArCWludCByZXQ7DQo+IA0KPiAtCWlm
ICghdGVzdF9iaXQoSENJX1VBUlRfUkVHSVNURVJFRCwgJmh1LT5mbGFncykpDQo+ICsJQlRfREJH
KCJiY21faDRfcmVjdiBodSAlcCBsZW4gJWQiLCBodSwgY291bnQpOw0KPiArDQo+ICsJaWYgKCF0
ZXN0X2JpdChIQ0lfVUFSVF9SRUdJU1RFUkVELCAmaHUtPmZsYWdzKSkgew0KPiArCQlCVF9EQkco
Img0X3JlY3YgVUFSVCBub3QgcmVnaXN0ZXJlZCEiKTsNCj4gCQlyZXR1cm4gLUVVTkFUQ0g7DQo+
ICsJfQ0KDQpBbGwgdGhlc2UgZGVidWcgYWRkaXRpb25zIGFyZSBjbHV0dGVyaW5nIHRoZSBwYXRj
aCByZXZpZXcuIE1ha2VzIGl0IGEgbG90IGhhcmR3YXJlIGZvciBtZSB0byByZXZpZXcuIEl0IGlz
IGZpbmUgaWYgeW91IGtlZXAgdGhlbSBpbiBhIHNlcmllcyBhcyB0ZW1wb3JhcnkgcGF0Y2hlcywg
YnV0IGhlcmUgaXQgaXMganVzdCBjYXVzaW5nIGV4dHJhIHdvcmsgZm9yIG1lLg0KDQpJRjogUmVt
b3ZlZCBleHRyYSBkZWJ1ZyBzdGF0ZW1lbnRzLg0KDQo+IA0KPiAtCWJjbS0+cnhfc2tiID0gaDRf
cmVjdl9idWYoaHUtPmhkZXYsIGJjbS0+cnhfc2tiLCBkYXRhLCBjb3VudCwNCj4gLQkJCQkgIGJj
bV9yZWN2X3BrdHMsIEFSUkFZX1NJWkUoYmNtX3JlY3ZfcGt0cykpOw0KPiAtCWlmIChJU19FUlIo
YmNtLT5yeF9za2IpKSB7DQo+IC0JCWludCBlcnIgPSBQVFJfRVJSKGJjbS0+cnhfc2tiKTsNCj4g
LQkJQlRfRVJSKCIlczogRnJhbWUgcmVhc3NlbWJseSBmYWlsZWQgKCVkKSIsIGh1LT5oZGV2LT5u
YW1lLCBlcnIpOw0KPiAtCQlyZXR1cm4gZXJyOw0KPiArCS8qIE1ha2Ugc3VyZSB3ZSdyZSByZXN1
bWVkICovDQo+ICsJYmNtX2Vuc3VyZV93YWtldXAoaHUpOw0KPiArDQo+ICsJLyogSWYgd2UncmUg
aW4gdGhlIGFjdGl2ZSBwaGFzZSwgcGFyc2Ugd2hhdCB3ZSBnZXQgKi8NCj4gKwlpZiAoaDQtPnBh
cnNlX3JlY3YpIHsNCj4gKwkJcGFyc2VfZnJhZ21lbnQoaHUsIGRhdGEsIGNvdW50KTsNCj4gKwl9
IGVsc2Ugew0KPiArCQlyZXQgPSBoY2lfcmVjdl9zdHJlYW1fZnJhZ21lbnQoaHUtPmhkZXYsIGRh
dGEsIGNvdW50KTsNCg0KVGhlIGhjaV9yZWN2X3N0cmVhbV9mcmFnZW1lbnQgZnVuY3Rpb24gaXMg
Z29uZSBmcm9tIHVwc3RyZWFtLiBZb3UgY2FuIG5vdCB1c2UgaXQgYW55bW9yZS4NCg0KPiArCQlp
ZiAocmV0IDwgMCkgew0KPiArCQkJQlRfRVJSKCJiY21faDRfcmVjdjogZnJhbWUgcmVhc3NlbWJs
eSBmYWlsZWQiKTsNCj4gKwkJCXJldHVybiByZXQ7DQo+ICsJCX0NCj4gCX0NCj4gDQo+IAlyZXR1
cm4gY291bnQ7DQo+IH0NCj4gDQo+IC1zdGF0aWMgaW50IGJjbV9lbnF1ZXVlKHN0cnVjdCBoY2lf
dWFydCAqaHUsIHN0cnVjdCBza19idWZmICpza2IpDQo+ICsvKg0KPiArICogTGluZSBkaXNjaXBs
aW5lIGlzIGdyYWJiaW5nIGEgcGFja2V0IGZyb20gdGhlIHR4IHF1ZXVlDQo+ICsgKi8NCj4gK3N0
YXRpYyBzdHJ1Y3Qgc2tfYnVmZiAqYmNtX2g0X2RlcXVldWUoc3RydWN0IGhjaV91YXJ0ICpodSkN
Cj4gew0KDQpUaGlzIHJlbmFtaW5nIGlzIHJlYWxseSBub3QgaGVscGluZyB0byBtYWtlIHRoZSBw
YXRjaCBsb29rIHNpbXBsZXIuDQoNCklGOiBSZXN0b3JlZCB0aGUgb3JpZ2luYWwgbmFtaW5nLg0K
DQo+IC0Jc3RydWN0IGJjbV9kYXRhICpiY20gPSBodS0+cHJpdjsNCj4gKwlzdHJ1Y3QgYmNtX2g0
X3N0cnVjdCAqaDQgPSBodS0+cHJpdjsNCj4gKwlpbnQgaXNfcWVtcHR5ID0gc2tiX3F1ZXVlX2Vt
cHR5KCZoNC0+dHhxKTsNCj4gDQo+IC0JQlRfREJHKCJodSAlcCBza2IgJXAiLCBodSwgc2tiKTsN
Cj4gKwlpZiAoIWlzX3FlbXB0eSkNCj4gKwkJQlRfREJHKCJiY21faDRfZGVxdWV1ZSB3aXRoIG5v
bi1lbXB0eSBxdWV1ZSIpOw0KPiArCXJldHVybiBza2JfZGVxdWV1ZSgmaDQtPnR4cSk7DQo+ICt9
DQo+IA0KPiAtCS8qIFByZXBlbmQgc2tiIHdpdGggZnJhbWUgdHlwZSAqLw0KPiAtCW1lbWNweShz
a2JfcHVzaChza2IsIDEpLCAmYnRfY2Ioc2tiKS0+cGt0X3R5cGUsIDEpOw0KPiAtCXNrYl9xdWV1
ZV90YWlsKCZiY20tPnR4cSwgc2tiKTsNCj4gKy8qDQo+ICsgKiBDYWxsYmFja3MgZnJvbSB0aGUg
QkNNQlRfVUFSVCBkZXZpY2UNCj4gKyAqLw0KPiANCj4gLQlyZXR1cm4gMDsNCj4gKy8qDQo+ICsg
KiBUaGUgcGxhdGZvcm0gaXMgc3VzcGVuZGluZy4gU3RvcCBVQVJUIGFjdGl2aXR5DQo+ICsgKi8N
Cj4gK3N0YXRpYyB2b2lkIHN1c3BlbmRfbm90aWZpY2F0aW9uKHZvaWQgKmNvbnRleHQpDQo+ICt7
DQo+ICsJc3RydWN0IGt0ZXJtaW9zIGt0ZXJtaW9zOw0KPiArCXN0cnVjdCBoY2lfdWFydCAqaHUg
PSAoc3RydWN0IGhjaV91YXJ0ICopY29udGV4dDsNCj4gKwlzdHJ1Y3QgYmNtX2g0X3N0cnVjdCAq
aDQgPSBodS0+cHJpdjsNCj4gKwlzdHJ1Y3QgdHR5X3N0cnVjdCAqdHR5ID0gaHUtPnR0eTsNCj4g
KwlpbnQgc3RhdHVzOw0KPiArCXVuc2lnbmVkIGludCBzZXQgPSAwOw0KPiArCXVuc2lnbmVkIGlu
dCBjbGVhciA9IDA7DQo+ICsNCj4gKwlCVF9EQkcoInN1c3BlbmRfbm90aWZpY2F0aW9uIHdpdGgg
aXNfc3VzcGVuZGVkICVkIiwgaDQtPmlzX3N1c3BlbmRlZCk7DQo+ICsNCj4gKwlpZiAoIWg0LT5w
YXJzLmNvbmZpZ3VyZV9zbGVlcCkNCj4gKwkJcmV0dXJuOw0KPiArDQo+ICsJaWYgKCFoNC0+aXNf
c3VzcGVuZGVkKSB7DQo+ICsJCWlmIChoNC0+cGFycy5tYW51YWxfZmMpIHsNCj4gKwkJCS8qIERp
c2FibGUgaGFyZHdhcmUgZmxvdyBjb250cm9sICovDQo+ICsJCQlrdGVybWlvcyA9IHR0eS0+dGVy
bWlvczsNCj4gKwkJCWt0ZXJtaW9zLmNfY2ZsYWcgJj0gfkNSVFNDVFM7DQo+ICsJCQlzdGF0dXMg
PSB0dHlfc2V0X3Rlcm1pb3ModHR5LCAma3Rlcm1pb3MpOw0KPiArCQkJaWYgKHN0YXR1cykNCj4g
KwkJCQlCVF9EQkcoInN1c3BlbmRfbm90aWZpY2F0aW9uIGRpcyBmYyBmYWlsICVkIiwNCj4gKwkJ
CQkgICAgICAgc3RhdHVzKTsNCj4gKwkJCWVsc2UNCj4gKwkJCQlCVF9EQkcoInN1c3BlbmRfbm90
aWZpY2F0aW9uIGh3IGZjIGRpc2FibGVkIik7DQo+ICsNCj4gKwkJCS8qIENsZWFyIFJUUyB0byBw
cmV2ZW50IHRoZSBkZXZpY2UgZnJvbSBzZW5kaW5nICovDQo+ICsJCQkvKiAobW9zdCBQQ3MgbmVl
ZCBPVVQyIHRvIGVuYWJsZSBpbnRlcnJ1cHRzKSAgICAqLw0KPiArCQkJc3RhdHVzID0gdHR5LT5k
cml2ZXItPm9wcy0+dGlvY21nZXQodHR5KTsNCj4gKwkJCUJUX0RCRygic3VzcGVuZF9ub3RpZmlj
YXRpb24gY3VyIHRpb2NtIDB4JXgiLCBzdGF0dXMpOw0KPiArCQkJc2V0ICY9IH4oVElPQ01fT1VU
MiB8IFRJT0NNX1JUUyk7DQo+ICsJCQljbGVhciA9IH5zZXQ7DQo+ICsJCQlzZXQgJj0gVElPQ01f
RFRSIHwgVElPQ01fUlRTIHwgVElPQ01fT1VUMSB8DQo+ICsJCQkJVElPQ01fT1VUMiB8IFRJT0NN
X0xPT1A7DQo+ICsJCQljbGVhciAmPSBUSU9DTV9EVFIgfCBUSU9DTV9SVFMgfCBUSU9DTV9PVVQx
IHwNCj4gKwkJCQlUSU9DTV9PVVQyIHwgVElPQ01fTE9PUDsNCj4gKwkJCXN0YXR1cyA9IHR0eS0+
ZHJpdmVyLT5vcHMtPnRpb2Ntc2V0KHR0eSwgc2V0LCBjbGVhcik7DQo+ICsJCQlpZiAoc3RhdHVz
KQ0KPiArCQkJCUJUX0RCRygic3VzcGVuZF9ub3RpZmljYXRpb24gY2xyIFJUUyBmYWlsICVkIiwN
Cj4gKwkJCQkgICAgICAgc3RhdHVzKTsNCj4gKwkJCWVsc2UNCj4gKwkJCQlCVF9EQkcoInN1c3Bl
bmRfbm90aWZpY2F0aW9uIFJUUyBjbGVhcmVkIik7DQo+ICsJCQlzdGF0dXMgPSB0dHktPmRyaXZl
ci0+b3BzLT50aW9jbWdldCh0dHkpOw0KPiArCQkJQlRfREJHKCJzdXNwZW5kX25vdGlmaWNhdGlv
biBlbmQgdGlvY20gMHgleCIsIHN0YXR1cyk7DQo+ICsJCX0NCg0KSSB0aGluayB0aGF0IGhjaV9s
ZGlzYy5jIHNob3VsZCBtYXliZSBwcm92aWRlIGNvbW1vbiBoZWxwZXJzIGZvciBjbGVhcmluZyBS
VFMgZXRjLiBJIHdvdWxkIHdvcmsgb24gbWFraW5nIHRoYXQgZ2VuZXJpYyBhbmQgc3BsaXR0aW5n
IGl0IG91dCBzbyBpdCBjYW4gYmUgdXNlZCBieSBvdGhlcnMuIFNlZW1zIGxpa2UgdGhlIEF0aGVy
b3MgVUFSVCBkcml2ZXIgbmVlZHMgdGhhdCBhcyB3ZWxsLiBIb3dldmVyIGl0IGxvb2tzIGEgbG90
IHNpbXBsZXIgdGhlcmUuIEFueXdheSwgaXQgc2hvdWxkIGJlIGdlbmVyYWwgYW5kIG5vdCBkdXBs
aWNhdGVkIGluIGV2ZXJ5IHN1YiBkcml2ZXIuDQoNCklGOiBBZ3JlZTogbGV0J3MgYXBwcm92ZSBy
ZWxldmFudCBJbnRlbCBjaGFuZ2VzIGFuZCBJJ2xsIHVzZSB0aGVtLg0KDQo+ICsNCj4gKwkJLyog
T25jZSB0aGlzIGNhbGxiYWNrIHJldHVybnMsIGRyaXZlciBzdXNwZW5kcyBCVCB2aWEgR1BJTyAq
Lw0KPiArCQloNC0+aXNfc3VzcGVuZGVkID0gdHJ1ZTsNCj4gKwl9DQo+ICt9DQo+ICsNCj4gKy8q
DQo+ICsgKiBUaGUgcGxhdGZvcm0gaXMgcmVzdW1pbmcuIFJlc3VtZSBVQVJUIGFjdGl2aXR5Lg0K
PiArICovDQo+ICtzdGF0aWMgdm9pZCByZXN1bWVfbm90aWZpY2F0aW9uKHZvaWQgKmNvbnRleHQp
DQo+ICt7DQo+ICsJc3RydWN0IGt0ZXJtaW9zIGt0ZXJtaW9zOw0KPiArCXN0cnVjdCBoY2lfdWFy
dCAqaHUgPSAoc3RydWN0IGhjaV91YXJ0ICopY29udGV4dDsNCj4gKwlzdHJ1Y3QgYmNtX2g0X3N0
cnVjdCAqaDQgPSBodS0+cHJpdjsNCj4gKwlzdHJ1Y3QgdHR5X3N0cnVjdCAqdHR5ID0gaHUtPnR0
eTsNCj4gKwlpbnQgc3RhdHVzOw0KPiArCXVuc2lnbmVkIGludCBzZXQgPSAwOw0KPiArCXVuc2ln
bmVkIGludCBjbGVhciA9IDA7DQo+ICsNCj4gKwlCVF9EQkcoInJlc3VtZV9ub3RpZmljYXRpb24g
d2l0aCBpc19zdXNwZW5kZWQgJWQiLCBoNC0+aXNfc3VzcGVuZGVkKTsNCj4gKw0KPiArCWlmICgh
aDQtPnBhcnMuY29uZmlndXJlX3NsZWVwKQ0KPiArCQlyZXR1cm47DQo+ICsNCj4gKwkvKiBXaGVu
IHRoaXMgY2FsbGJhY2sgZXhlY3V0ZXMsIHRoZSBkZXZpY2UgaGFzIHdva2VuIHVwIGFscmVhZHkg
Ki8NCj4gKwlpZiAoaDQtPmlzX3N1c3BlbmRlZCkgew0KPiArCQloNC0+aXNfc3VzcGVuZGVkID0g
ZmFsc2U7DQo+ICsNCj4gKwkJaWYgKGg0LT5wYXJzLm1hbnVhbF9mYykgew0KPiArCQkJc3RhdHVz
ID0gdHR5LT5kcml2ZXItPm9wcy0+dGlvY21nZXQodHR5KTsNCj4gKwkJCUJUX0RCRygicmVzdW1l
X25vdGlmaWNhdGlvbiBjdXIgdGlvY20gMHgleCIsIHN0YXR1cyk7DQo+ICsJCQlzZXQgfD0gKFRJ
T0NNX09VVDIgfCBUSU9DTV9SVFMpOw0KPiArCQkJY2xlYXIgPSB+c2V0Ow0KPiArCQkJc2V0ICY9
IFRJT0NNX0RUUiB8IFRJT0NNX1JUUyB8IFRJT0NNX09VVDEgfA0KPiArCQkJCVRJT0NNX09VVDIg
fCBUSU9DTV9MT09QOw0KPiArCQkJY2xlYXIgJj0gVElPQ01fRFRSIHwgVElPQ01fUlRTIHwgVElP
Q01fT1VUMSB8DQo+ICsJCQkJVElPQ01fT1VUMiB8IFRJT0NNX0xPT1A7DQo+ICsJCQlzdGF0dXMg
PSB0dHktPmRyaXZlci0+b3BzLT50aW9jbXNldCh0dHksIHNldCwgY2xlYXIpOw0KPiArCQkJaWYg
KHN0YXR1cykNCj4gKwkJCQlCVF9EQkcoInJlc3VtZV9ub3RpZmljYXRpb24gc2V0IFJUUyBmYWls
ICVkIiwNCj4gKwkJCQkgICAgICAgc3RhdHVzKTsNCj4gKwkJCWVsc2UNCj4gKwkJCQlCVF9EQkco
InJlc3VtZV9ub3RpZmljYXRpb24gUlRTIHNldCIpOw0KPiArDQo+ICsJCQkvKiBSZS1lbmFibGUg
aGFyZHdhcmUgZmxvdyBjb250cm9sICovDQo+ICsJCQlrdGVybWlvcyA9IHR0eS0+dGVybWlvczsN
Cj4gKwkJCWt0ZXJtaW9zLmNfY2ZsYWcgfD0gQ1JUU0NUUzsNCj4gKwkJCXN0YXR1cyA9IHR0eV9z
ZXRfdGVybWlvcyh0dHksICZrdGVybWlvcyk7DQo+ICsJCQlpZiAoc3RhdHVzKQ0KPiArCQkJCUJU
X0RCRygicmVzdW1lX25vdGlmaWNhdGlvbiBlbmFibGUgZmMgZmFpbCAlZCIsDQo+ICsJCQkJICAg
ICAgIHN0YXR1cyk7DQo+ICsJCQllbHNlDQo+ICsJCQkJQlRfREJHKCJyZXN1bWVfbm90aWZpY2F0
aW9uIGh3IGZjIHJlLWVuYWJsZWQiKTsNCj4gKwkJfQ0KPiArCX0NCj4gKw0KPiArCS8qIElmIHdl
J3JlIHJlc3VtZWQsIHRoZSBpZGxlIHRpbWVyIG11c3QgYmUgcnVubmluZyAqLw0KPiArCXN0YXR1
cyA9IG1vZF90aW1lcigmaDQtPnRpbWVyLCBqaWZmaWVzICsNCj4gKwkgICAgbXNlY3NfdG9famlm
ZmllcyhoNC0+cGFycy5pZGxlX3RpbWVvdXRfaW5fc2VjcyAqIDEwMDApKTsNCj4gfQ0KPiANCj4g
LXN0YXRpYyBzdHJ1Y3Qgc2tfYnVmZiAqYmNtX2RlcXVldWUoc3RydWN0IGhjaV91YXJ0ICpodSkN
Cj4gKy8qDQo+ICsgKiBUaGUgQlQgZGV2aWNlIGlzIHJlc3VtaW5nLiBSZXN1bWUgVUFSVCBhY3Rp
dml0eSBpZiBzdXNwZW5kZWQNCj4gKyAqLw0KPiArc3RhdGljIHZvaWQgd2FrZXVwX25vdGlmaWNh
dGlvbih2b2lkICpjb250ZXh0KQ0KPiB7DQo+IC0Jc3RydWN0IGJjbV9kYXRhICpiY20gPSBodS0+
cHJpdjsNCj4gKwlzdHJ1Y3Qga3Rlcm1pb3Mga3Rlcm1pb3M7DQo+ICsJc3RydWN0IGhjaV91YXJ0
ICpodSA9IChzdHJ1Y3QgaGNpX3VhcnQgKiljb250ZXh0Ow0KPiArCXN0cnVjdCBiY21faDRfc3Ry
dWN0ICpoNCA9IGh1LT5wcml2Ow0KPiArCXN0cnVjdCB0dHlfc3RydWN0ICp0dHkgPSBodS0+dHR5
Ow0KPiArCWludCBzdGF0dXM7DQo+ICsJdW5zaWduZWQgaW50IHNldCA9IDA7DQo+ICsJdW5zaWdu
ZWQgaW50IGNsZWFyID0gMDsNCj4gKw0KPiArCWlmICghaDQtPnBhcnMuY29uZmlndXJlX3NsZWVw
KQ0KPiArCQlyZXR1cm47DQo+ICsNCj4gKwlzdGF0dXMgPSB0dHktPmRyaXZlci0+b3BzLT50aW9j
bWdldCh0dHkpOw0KPiArCUJUX0RCRygid2FrZXVwX25vdGlmaWNhdGlvbiBodSAlcCBjdXJyZW50
IHRpb2NtIDB4JXgiLCBodSwgc3RhdHVzKTsNCj4gKwlpZiAoaDQtPmlzX3N1c3BlbmRlZCkgew0K
PiArCQlpZiAoaDQtPnBhcnMubWFudWFsX2ZjKSB7DQo+ICsJCQlzZXQgfD0gKFRJT0NNX09VVDIg
fCBUSU9DTV9SVFMpOw0KPiArCQkJY2xlYXIgPSB+c2V0Ow0KPiArCQkJc2V0ICY9IFRJT0NNX0RU
UiB8IFRJT0NNX1JUUyB8IFRJT0NNX09VVDEgfA0KPiArCQkJCVRJT0NNX09VVDIgfCBUSU9DTV9M
T09QOw0KPiArCQkJY2xlYXIgJj0gVElPQ01fRFRSIHwgVElPQ01fUlRTIHwgVElPQ01fT1VUMSB8
DQo+ICsJCQkJVElPQ01fT1VUMiB8IFRJT0NNX0xPT1A7DQo+ICsJCQlzdGF0dXMgPSB0dHktPmRy
aXZlci0+b3BzLT50aW9jbXNldCh0dHksIHNldCwgY2xlYXIpOw0KPiArCQkJaWYgKHN0YXR1cykN
Cj4gKwkJCQlCVF9EQkcoIndha2V1cF9ub3RpZmljYXRpb24gc2V0IFJUUyBmYWlsICVkIiwNCj4g
KwkJCQkgICAgICAgc3RhdHVzKTsNCj4gKwkJCWVsc2UNCj4gKwkJCQlCVF9EQkcoIndha2V1cF9u
b3RpZmljYXRpb24gUlRTIHNldCIpOw0KPiArDQo+ICsJCQkvKiBSZS1lbmFibGUgaGFyZHdhcmUg
ZmxvdyBjb250cm9sICovDQo+ICsJCQlrdGVybWlvcyA9IHR0eS0+dGVybWlvczsNCj4gKwkJCWt0
ZXJtaW9zLmNfY2ZsYWcgfD0gQ1JUU0NUUzsNCj4gKwkJCXN0YXR1cyA9IHR0eV9zZXRfdGVybWlv
cyh0dHksICZrdGVybWlvcyk7DQo+ICsJCQlpZiAoc3RhdHVzKQ0KPiArCQkJCUJUX0RCRygid2Fr
ZXVwX25vdGlmaWNhdGlvbiBmYy1lbiBmYWlsdXJlICVkIiwNCj4gKwkJCQkgICAgICAgc3RhdHVz
KTsNCj4gKwkJCWVsc2UNCj4gKwkJCQlCVF9EQkcoIndha2V1cF9ub3RpZmljYXRpb24gaHcgZmMg
cmUtZW5hYmxlZCIpOw0KPiArCQl9DQo+ICsNCj4gKwkJaDQtPmlzX3N1c3BlbmRlZCA9IGZhbHNl
Ow0KPiArCX0NCj4gDQo+IC0JcmV0dXJuIHNrYl9kZXF1ZXVlKCZiY20tPnR4cSk7DQo+ICsJLyog
SWYgd2UncmUgcmVzdW1lZCwgdGhlIGlkbGUgdGltZXIgbXVzdCBiZSBydW5uaW5nICovDQo+ICsJ
c3RhdHVzID0gbW9kX3RpbWVyKCZoNC0+dGltZXIsIGppZmZpZXMgKyBtc2Vjc190b19qaWZmaWVz
KA0KPiArCQkJICAgaDQtPnBhcnMuaWRsZV90aW1lb3V0X2luX3NlY3MgKiAxMDAwKSk7DQo+IH0N
Cj4gDQo+IC1zdGF0aWMgY29uc3Qgc3RydWN0IGhjaV91YXJ0X3Byb3RvIGJjbV9wcm90byA9IHsN
Cj4gKy8qDQo+ICsgKiBEZXZpY2Ugc2V0dXAgdGhhdCBmb2xsb3dzIHRoZSBwcm90b2NvbCBvcGVu
DQo+ICsgKi8NCj4gK3N0YXRpYyBpbnQgYmNtX2g0X3NldHVwKHN0cnVjdCBoY2lfdWFydCAqaHUp
DQo+ICt7DQo+ICsJc3RydWN0IGJjbV9oNF9zdHJ1Y3QgKmg0ID0gaHUtPnByaXY7DQo+ICsJaW50
IHN0YXR1czsNCj4gKwlzdHJ1Y3Qgc2tfYnVmZiAqc2tiOw0KPiArCXVuc2lnbmVkIGNoYXIgc2xl
ZXBfcGFyc1tdID0gew0KPiArCTB4MDEsICAgICAgIC8qIHNsZWVwIG1vZGUgMT1VQVJUICovDQo+
ICsJMHgwMiwgICAgICAgLyogaWRsZSB0aHJlc2hvbGQgSE9TVCAodmFsdWUgKiAzMDBtcykgKi8N
Cj4gKwkweDAyLCAgICAgICAvKiBpZGxlIHRocmVzaG9sZCBIQyAgICh2YWx1ZSAqIDMwMG1zKSAq
Lw0KPiArCTB4MDEsICAgICAgIC8qIEJUX1dBS0UgYWN0aXZlIG1vZGUgLSAxPWFjdGl2ZSBoaWdo
LCAwID0gYWN0aXZlIGxvdyAqLw0KPiArCTB4MDAsICAgICAgIC8qIEhPU1RfV0FLRSBhY3RpdmUg
bW9kZSAtIDE9YWN0aXZlIGhpZ2gsIDAgPSBhY3RpdmUgbG93ICovDQo+ICsJMHgwMSwgICAgICAg
LyogQWxsb3cgaG9zdCBzbGVlcCBkdXJpbmcgU0NPIC0gRkFMU0UgKi8NCj4gKwkweDAxLCAgICAg
ICAvKiBjb21iaW5lIHNsZWVwIG1vZGUgYW5kIExQTSAtIDEgPT0gVFJVRSAqLw0KPiArCTB4MDAs
ICAgICAgIC8qIGVuYWJsZSB0cmlzdGF0ZSBjb250cm9sIG9mIFVBUlQgVFggbGluZSAtIEZBTFNF
ICovDQo+ICsJMHgwMCwgICAgICAgLyogVVNCIGF1dG8tc2xlZXAgb24gVVNCIFNVU1BFTkQgKi8N
Cj4gKwkweDAwLCAgICAgICAvKiBVU0IgVVNCIFJFU1VNRSB0aW1lb3V0IChzZWNvbmRzKSAqLw0K
PiArCTB4MDAsICAgICAgIC8qIFB1bHNlZCBIb3N0IFdha2UgKi8NCj4gKwkweDAwICAgICAgICAv
KiBFbmFibGUgQnJlYWsgVG8gSG9zdCAqLw0KPiArCX07DQo+ICsJdW5zaWduZWQgY2hhciBwY21f
aW50X3BhcnNbXSA9IHsNCj4gKwkweDAwLCAgICAgICAvKiAwPVBDTSByb3V0aW5nLCAxPVNDTyBv
dmVyIEhDSSAqLw0KPiArCTB4MDIsICAgICAgIC8qIDA9MTI4S2JwcywxPTI1NkticHMsMj01MTJL
YnBzLDM9MTAyNEticHMsND0yMDQ4S2JwcyAqLw0KPiArCTB4MDAsICAgICAgIC8qIHNob3J0IGZy
YW1lIHN5bmMgIDA9c2hvcnQsIDE9bG9uZyAqLw0KPiArCTB4MDAsICAgICAgIC8qIHN5bmMgbW9k
ZSAgICAgICAgIDA9c2xhdmUsIDE9bWFzdGVyICovDQo+ICsJMHgwMCAgICAgICAgLyogY2xvY2sg
bW9kZSAgICAgICAgMD1zbGF2ZSwgMT1tYXN0ZXIgKi8NCj4gKwl9Ow0KPiArCXVuc2lnbmVkIGNo
YXIgcGNtX2Zvcm1hdF9wYXJzW10gPSB7DQo+ICsJMHgwMCwgICAgICAgLyogTFNCX0ZpcnN0IDE9
VFJVRSwgMD1GQUxTRSAqLw0KPiArCTB4MDAsICAgICAgIC8qIEZpbGxfVmFsdWUgKHVzZSAwLTcg
Zm9yIGZpbGwgYml0cyB2YWx1ZSkgKi8NCj4gKwkweDAyLCAgICAgICAvKiBGaWxsX01ldGhvZCAg
ICgyPXNpZ24gZXh0ZW5kZWQpICovDQo+ICsJMHgwMywgICAgICAgLyogRmlsbF9OdW0gICAgICAj
IG9mIGZpbGwgYml0cyAwLTMpICovDQo+ICsJMHgwMSAgICAgICAgLyogUmlnaHRfSnVzdGlmeSAx
PVRSVUUsIDA9RkFMU0UgKi8NCj4gKwl9Ow0KDQpUaGlzIGlzIHZpb2xhdGluZyB0aGUgaW5kZW50
YXRpb24gY29kaW5nIHN0eWxlLg0KDQpJRjogVGhhdCdzIGNvcnJlY3QuIEJ1dCBpdCBkb2VzIGFs
bG93IHVzIHRvIGhhdmUgbW9yZSByb29tIGZvciB0aGUgcGFyYW1ldGVyIGRlc2NyaXB0aW9ucy4N
Cg0KPiArCXVuc2lnbmVkIGNoYXIgdGltZV9zbG90X251bWJlciA9IDA7DQo+ICsNCj4gKwlCVF9E
QkcoImJjbV9oNF9zZXR1cCBodSAlcCIsIGh1KTsNCj4gKw0KPiArCS8qIEJyaW5nIHRoZSBVQVJU
IGludG8ga25vd24gZGVmYXVsdCBzdGF0ZSAqLw0KPiArCXN0YXR1cyA9IGJ0YmNtX2luaXRfdWFy
dChodSk7DQo+ICsJaWYgKHN0YXR1cykgew0KPiArCQlCVF9EQkcoImJjbV9oNF9zZXR1cCBmYWls
ZWQgdG8gaW5pdCBCVCBkZXZpY2UgJWQiLCBzdGF0dXMpOw0KPiArCQlyZXR1cm4gc3RhdHVzOw0K
PiArCX0NCj4gKw0KPiArCS8qIEJhc2ljIHNhbml0eSBjaGVjayAqLw0KPiArCXNrYiA9IF9faGNp
X2NtZF9zeW5jKGh1LT5oZGV2LCBIQ0lfT1BfUkVTRVQsIDAsIE5VTEwsIEhDSV9JTklUX1RJTUVP
VVQpOw0KPiArCWlmIChJU19FUlIoc2tiKSkgew0KPiArCQlzdGF0dXMgPSBQVFJfRVJSKHNrYik7
DQo+ICsJCUJUX0VSUigiYmNtX2g0X3NldHVwIEhDSSBSZXNldCBmYWlsZWQgKCVkKSIsIHN0YXR1
cyk7DQo+ICsJCXJldHVybiBzdGF0dXM7DQo+ICsJfQ0KPiArCWtmcmVlX3NrYihza2IpOw0KPiAr
CUJUX0RCRygiYmNtX2g0X3NldHVwIEhDSSBSZXNldCBzdWNjZWVkZWQiKTsNCj4gKw0KPiArCS8q
IFNldCB0aGUgbmV3IGJhdWQgcmF0ZSAqLw0KPiArCXN0YXR1cyA9IGJ0YmNtX3NldF9iYXVkX3Jh
dGUoaHUsDQo+ICsJCQkJICAgICBoNC0+cGFycy5iYXVkX3JhdGVfYmVmb3JlX2NvbmZpZ19kb3du
bG9hZCk7DQo+ICsJaWYgKHN0YXR1cykgew0KPiArCQlCVF9FUlIoImJjbV9oNF9zZXR1cCBzZXRf
YmF1ZF9yYXRlIGZhaWlsdXJlICVkIiwgc3RhdHVzKTsNCj4gKwkJcmV0dXJuIHN0YXR1czsNCj4g
Kwl9DQo+ICsNCj4gKwlodS0+aGRldi0+c2V0X2JkYWRkciA9IGJ0YmNtX3NldF9iZGFkZHI7DQo+
ICsNCj4gKwkvKiBEb3dubG9hZCB0aGUgZmlybXdhcmUgYW5kIHJlY29uZmlndXJlIHRoZSBVQVJU
IGFmdGVyd2FyZHMgKi8NCj4gKwlzdGF0dXMgPSBidGJjbV9zZXR1cF9wYXRjaHJhbShodS0+aGRl
dik7DQo+ICsJaWYgKHN0YXR1cykgew0KPiArCQlCVF9FUlIoImJjbV9oNF9zZXR1cCBzZXR1cF9w
YXRjaHJhbSBmYWlpbHVyZSAlZCIsIHN0YXR1cyk7DQo+ICsJCXJldHVybiBzdGF0dXM7DQo+ICsJ
fQ0KPiArDQo+ICsJLyogQ29uZmlndXJlIFNDTyBQQ00gcGFyYW1ldGVycyAqLw0KPiArCWlmICho
NC0+cGFycy5jb25maWd1cmVfYXVkaW8pIHsNCj4gKwkJcGNtX2ludF9wYXJzWzBdID0gaDQtPnBh
cnMuUENNUm91dGluZzsNCj4gKwkJcGNtX2ludF9wYXJzWzFdID0gaDQtPnBhcnMuUENNSW5DYWxs
Qml0Y2xvY2s7DQo+ICsJCXBjbV9pbnRfcGFyc1syXSA9IGg0LT5wYXJzLlBDTVNob3J0RnJhbWVT
eW5jOw0KPiArCQlwY21faW50X3BhcnNbM10gPSBoNC0+cGFycy5QQ01TeW5jTW9kZTsNCj4gKwkJ
cGNtX2ludF9wYXJzWzRdID0gaDQtPnBhcnMuUENNQ2xvY2tNb2RlOw0KDQpJIHJlYWxseSBnZXQg
dGhlIGZlZWxpbmcgd2Ugd2FudCBhIHByb3BlciBzdHJ1Y3QgZm9yIHRoaXMgSENJIGNvbW1hbmQu
IEkgYWxzbyB3b25kZXIgaWYgdGhpcyBzaG91bGQgYmUgYWN0dWFsIHNvbWUgZ2VuZXJpYyBmdW5j
dGlvbiBleHBvc2VkIGJ5IGJ0YmNtLmtvLiBJdCBpcyByZWFsbHkgbm90IGxpbWl0ZWQgdG8gVUFS
VCBkZXZpY2UuIEl0IGlzIGFsc28gcG9zc2libGUgdG8gYnVpbGQgYW4gVVNCIGJhc2VkIHBsYXRm
b3JtIHRoYXQgaGFzIFNDTyByb3V0ZWQgb3ZlciBQQ00uDQoNCklGOiBUaGlzIGlzIG5vdCBzdXBw
b3J0ZWQgYnkgdGhlIEJyb2FkY29tIFVTQiBkZXZpY2VzLg0KDQo+ICsJCXNrYiA9IF9faGNpX2Nt
ZF9zeW5jKGh1LT5oZGV2LCAweGZjMWMsIHNpemVvZihwY21faW50X3BhcnMpLA0KPiArCQkJCSAg
ICAgcGNtX2ludF9wYXJzLCBIQ0lfSU5JVF9USU1FT1VUKTsNCj4gKwkJaWYgKElTX0VSUihza2Ip
KSB7DQo+ICsJCQlzdGF0dXMgPSBQVFJfRVJSKHNrYik7DQo+ICsJCQlCVF9FUlIoImJjbV9oNF9z
ZXR1cCBQQ00gSU5UIFZTQyBmYWlsZWQgKCVkKSIsIHN0YXR1cyk7DQo+ICsJCQlyZXR1cm4gc3Rh
dHVzOw0KPiArCQl9DQo+ICsJCWtmcmVlX3NrYihza2IpOw0KPiArCQlCVF9EQkcoImJjbV9oNF9z
ZXR1cCBQQ00gSU5UIFBhcmFtZXRlcnMgVlNDIHN1Y2NlZWRlZCIpOw0KPiArDQo+ICsJCXBjbV9m
b3JtYXRfcGFyc1swXSA9IGg0LT5wYXJzLlBDTUxTQkZpcnN0Ow0KPiArCQlwY21fZm9ybWF0X3Bh
cnNbMV0gPSBoNC0+cGFycy5QQ01GaWxsVmFsdWU7DQo+ICsJCXBjbV9mb3JtYXRfcGFyc1syXSA9
IGg0LT5wYXJzLlBDTUZpbGxNZXRob2Q7DQo+ICsJCXBjbV9mb3JtYXRfcGFyc1szXSA9IGg0LT5w
YXJzLlBDTUZpbGxOdW07DQo+ICsJCXBjbV9mb3JtYXRfcGFyc1s0XSA9IGg0LT5wYXJzLlBDTVJp
Z2h0SnVzdGlmeTsNCj4gKwkJc2tiID0gX19oY2lfY21kX3N5bmMoaHUtPmhkZXYsIDB4ZmMxZSwg
c2l6ZW9mKHBjbV9mb3JtYXRfcGFycyksDQo+ICsJCQkJICAgICBwY21fZm9ybWF0X3BhcnMsIEhD
SV9JTklUX1RJTUVPVVQpOw0KPiArCQlpZiAoSVNfRVJSKHNrYikpIHsNCj4gKwkJCXN0YXR1cyA9
IFBUUl9FUlIoc2tiKTsNCj4gKwkJCUJUX0VSUigiYmNtX2g0X3NldHVwIFBDTSBGb3JtYXQgVlND
IGZhaWxlZCAoJWQpIiwNCj4gKwkJCSAgICAgICBzdGF0dXMpOw0KPiArCQkJcmV0dXJuIHN0YXR1
czsNCj4gKwkJfQ0KPiArCQlrZnJlZV9za2Ioc2tiKTsNCj4gKwkJQlRfREJHKCJiY21faDRfc2V0
dXAgUENNIEZvcm1hdCBWU0Mgc3VjY2VlZGVkIik7DQo+ICsNCj4gKwkJc2tiID0gX19oY2lfY21k
X3N5bmMoaHUtPmhkZXYsIDB4ZmMyMiwgc2l6ZW9mKHRpbWVfc2xvdF9udW1iZXIpLA0KPiArCQkJ
CSAgICAgJnRpbWVfc2xvdF9udW1iZXIsIEhDSV9JTklUX1RJTUVPVVQpOw0KPiArCQlpZiAoSVNf
RVJSKHNrYikpIHsNCj4gKwkJCXN0YXR1cyA9IFBUUl9FUlIoc2tiKTsNCj4gKwkJCUJUX0VSUigi
YmNtX2g0X3NldHVwIFNDTyBUaW1lIFNsb3QgVlNDIGZhaWxlZCAoJWQpIiwNCj4gKwkJCSAgICAg
ICBzdGF0dXMpOw0KPiArCQkJcmV0dXJuIHN0YXR1czsNCj4gKwkJfQ0KPiArCQlrZnJlZV9za2Io
c2tiKTsNCj4gKwkJQlRfREJHKCJiY21faDRfc2V0dXAgU0NPIFRpbWUgU2xvdCBWU0Mgc3VjY2Vl
ZGVkIik7DQo+ICsJfQ0KPiArDQo+ICsJLyogQ29uZmlndXJlIGRldmljZSdzIHN1c3BlbmQvcmVz
dW1lIG9wZXJhdGlvbiAqLw0KPiArCWlmIChoNC0+cGFycy5jb25maWd1cmVfc2xlZXApIHsNCj4g
KwkJLyogT3ZlcnJpZGUgdGhlIGRlZmF1bHQgKi8NCj4gKwkJc2xlZXBfcGFyc1szXSA9ICh1bnNp
Z25lZCBjaGFyKSFoNC0+cGFycy5idF93YWtlX2FjdGl2ZV9sb3c7DQo+ICsJCXNsZWVwX3BhcnNb
NF0gPSAodW5zaWduZWQgY2hhcikhaDQtPnBhcnMuZGV2X3dha2VfYWN0aXZlX2xvdzsNCj4gKwkJ
c2tiID0gX19oY2lfY21kX3N5bmMoaHUtPmhkZXYsIDB4ZmMyNywgc2l6ZW9mKHNsZWVwX3BhcnMp
LA0KPiArCQkJCSAgICAgc2xlZXBfcGFycywgSENJX0lOSVRfVElNRU9VVCk7DQo+ICsJCWlmIChJ
U19FUlIoc2tiKSkgew0KPiArCQkJc3RhdHVzID0gUFRSX0VSUihza2IpOw0KPiArCQkJQlRfRVJS
KCJiY21faDRfc2V0dXAgU2xlZXAgVlNDIGZhaWxlZCAoJWQpIiwgc3RhdHVzKTsNCj4gKwkJCXJl
dHVybiBzdGF0dXM7DQo+ICsJCX0NCj4gKwkJa2ZyZWVfc2tiKHNrYik7DQo+ICsJCUJUX0RCRygi
YmNtX2g0X3NldHVwIFNldCBTbGVlcCBQYXJhbWV0ZXJzIFZTQyBzdWNjZWVkZWQiKTsNCj4gKwl9
DQo+ICsNCj4gKwlyZXR1cm4gMDsNCj4gK30NCj4gKw0KPiArLyoNCj4gKyAqIFByb3RvY29sIGNh
bGxiYWNrcw0KPiArICovDQo+ICtzdGF0aWMgc3RydWN0IGhjaV91YXJ0X3Byb3RvIGg0cCA9IHsN
Cg0KS2VlcCB0aGlzIGFzIGJjbV9wcm90by4NCg0KSUY6IFJlc3RvcmVkLg0KDQo+IAkuaWQJCT0g
SENJX1VBUlRfQkNNLA0KPiAtCS5uYW1lCQk9ICJCQ00iLA0KPiAtCS5vcGVuCQk9IGJjbV9vcGVu
LA0KPiAtCS5jbG9zZQkJPSBiY21fY2xvc2UsDQo+IC0JLmZsdXNoCQk9IGJjbV9mbHVzaCwNCj4g
LQkuc2V0dXAJCT0gYmNtX3NldHVwLA0KPiAtCS5yZWN2CQk9IGJjbV9yZWN2LA0KPiAtCS5lbnF1
ZXVlCT0gYmNtX2VucXVldWUsDQo+IC0JLmRlcXVldWUJPSBiY21fZGVxdWV1ZSwNCj4gKwkubmFt
ZQkJPSAiVUFSVEJDTeKAnSwNCg0KUGxlYXNlIHN0b3AgcmVuYW1pbmcgdGhpbmdzLiBUaGlzIGlz
IGEgVUFSVCBkcml2ZXIgYW5kIHRodXMgbm8gbmVlZCB0byByZXBlYXQgdGhhdC4NCg0KSUY6IFJl
c3RvcmVkLiBBZ2FpbiwgSSB3YXMgbm90IHJlbmFtaW5nIGFueXRoaW5nOiBJIHdyb3RlIGl0IGZp
cnN0IGJlZm9yZSB5b3UuDQoNCj4gKwkub3BlbgkJPSBiY21faDRfb3BlbiwNCj4gKwkuY2xvc2UJ
CT0gYmNtX2g0X2Nsb3NlLA0KPiArCS5mbHVzaAkJPSBiY21faDRfZmx1c2gsDQo+ICsJLnNldHVw
CQk9IGJjbV9oNF9zZXR1cCwNCj4gKwkucmVjdgkJPSBiY21faDRfcmVjdiwNCj4gKwkuZW5xdWV1
ZQk9IGJjbV9oNF9lbnF1ZXVlLA0KPiArCS5kZXF1ZXVlCT0gYmNtX2g0X2RlcXVldWUsDQo+IH07
DQo+IA0KPiArLyoNCj4gKyAqIFByb3RvY29sIGluaXQNCj4gKyAqLw0KPiBpbnQgX19pbml0IGJj
bV9pbml0KHZvaWQpDQo+IHsNCj4gLQlyZXR1cm4gaGNpX3VhcnRfcmVnaXN0ZXJfcHJvdG8oJmJj
bV9wcm90byk7DQo+ICsJaW50IHN0YXR1cyA9IGhjaV91YXJ0X3JlZ2lzdGVyX3Byb3RvKCZoNHAp
Ow0KPiArDQo+ICsJaWYgKCFzdGF0dXMpDQo+ICsJCUJUX0lORk8oIkJyb2FkY29tIEg0IHByb3Rv
Y29sIGluaXRpYWxpemVkIik7DQo+ICsJZWxzZQ0KPiArCQlCVF9FUlIoIkJyb2FkY29tIEg0IHBy
b3RvY29sIHJlZ2lzdHJhdGlvbiBmYWlsZWQgJWQiLCBzdGF0dXMpOw0KPiArDQo+ICsJcmV0dXJu
IHN0YXR1czsNCg0KSSBtb3ZlIHRoaXMgZXJyb3IgcHJpbnRpbmcgaW50byBnZW5lcmljIGNvZGUg
YW5kIHNvIHBsZWFzZSBkbyBub3QgbW9kaWZ5IHRoaXMgYW5kIHJldmVydCB3aGF0IEkgZGlkLg0K
DQpJRjogRG9uZS4NCg0KPiB9DQo+IA0KPiArLyoNCj4gKyAqIFByb3RvY29sIHNodXRkb3duDQo+
ICsgKi8NCj4gaW50IF9fZXhpdCBiY21fZGVpbml0KHZvaWQpDQo+IHsNCj4gLQlyZXR1cm4gaGNp
X3VhcnRfdW5yZWdpc3Rlcl9wcm90bygmYmNtX3Byb3RvKTsNCj4gKwlCVF9JTkZPKCJCcm9hZGNv
bSBINCBwcm90b2NvbCBkZS1pbml0aWFsaXplZCIpOw0KPiArCXJldHVybiBoY2lfdWFydF91bnJl
Z2lzdGVyX3Byb3RvKCZoNHApOw0KPiB9DQo+ICsNCg0KTm8gZXh0cmEgZW1wdHkgbGluZSBhdCB0
aGUgZW5kIG9mIGEgZmlsZS4NCg0KSUY6IFJlbW92ZWQuDQoNClJlZ2FyZHMNCg0KTWFyY2VsDQoN
Cg==

2015-05-11 19:14:23

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC 1/2] Bluetooth UART Device Tree bindings

Hi Ilya,

> Device Tree bindings to configure the Bluetooth UART device.
>
> Signed-off-by: Ilya Faenson <[email protected]>
> ---
> .../devicetree/bindings/net/bluetooth/btbcm.txt | 54 ++++++++++++++++++++++
> 1 file changed, 54 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/bluetooth/btbcm.txt
>
> diff --git a/Documentation/devicetree/bindings/net/bluetooth/btbcm.txt b/Documentation/devicetree/bindings/net/bluetooth/btbcm.txt
> new file mode 100644
> index 0000000..cc9f225
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/bluetooth/btbcm.txt
> @@ -0,0 +1,54 @@
> +btbcm
> +------
> +
> +Required properties:
> +
> + - compatible : must be "brcm,brcm-bt-uart".
> + - tty : tty device connected to this Bluetooth device.
> +
> +Optional properties:
> +
> + - bt-host-wake-gpios : bt-host-wake input GPIO to be used as an interrupt.
> +
> + - bt-wake-gpios : bt-wake output GPIO to be used to suspend / resume device.
> +
> + - reg-on-gpios : reg-on output GPIO to be used to power device on/off.
> +
> + - baud-rate-before-config-download : initial Bluetooth device baud rate.
> + Default: 3000000.
> +
> + - manual-fc : flow control UART in suspend / resume scenarios.
> + Default: 0.
> +
> + - configure-sleep : configure suspend / resume flag.
> + Default: 0.
> +
> + - configure-audio : configure platform PCM SCO flag.
> + Default: 0.
> +
> + - PCM* : SCO PCM platform parameters. Work with Broadcom on setting.
> + Defaults: see the example below.

I think at some point this needs to be reviewed by the DT guys as well. They surely want to have some say in the naming and how to deal with the Bluetooth devices behind UART controllers.

Regards

Marcel


2015-05-11 19:03:43

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC 2/2] Broadcom Bluetooth UART device driver

Hi Ilya,

> This code implements the Broadcom Bluetooth device driver.
> It manages device configuration, firmware download and
> power management.
>
> Signed-off-by: Ilya Faenson <[email protected]>
> ---
> drivers/bluetooth/Kconfig | 35 +-
> drivers/bluetooth/Makefile | 1 +
> drivers/bluetooth/btbcm.c | 173 ++++++++-
> drivers/bluetooth/btbcm.h | 19 +-
> drivers/bluetooth/btbcm_uart.c | 679 +++++++++++++++++++++++++++++++++
> drivers/bluetooth/btbcm_uart.h | 90 +++++
> drivers/bluetooth/hci_bcm.c | 838 +++++++++++++++++++++++++++++++++++++----
> 7 files changed, 1745 insertions(+), 90 deletions(-)
> create mode 100644 drivers/bluetooth/btbcm_uart.c
> create mode 100644 drivers/bluetooth/btbcm_uart.h
>
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index ed5c273..1bfe36b 100644
> --- a/drivers/bluetooth/Kconfig
> +++ b/drivers/bluetooth/Kconfig
> @@ -8,6 +8,12 @@ config BT_INTEL
> config BT_BCM
> tristate
> select FW_LOADER
> + help
> + This feature is required if you want to use Broadcom Bluetooth
> + over both USB and UART.

I think this should say “over either USB or UART”.

> +
> + Say Y here to compile support for Broadcom Bluetooth
> + kernel or say M to compile it as module (btusb).

The reason why I never bothered adding a help text description to this entry is because it is a hidden entry. Selecting either the Broadcom USB or its UART version causes this module to be built. You will never see this entry in the kernel configuration user interface.

That means you can not actually select Y or M or N here. It is selected for you based on other choices made by actual exposed options. So it is up to you if you want to keep this or not. If you do, then this should be a separate patch since it is fixing some existing piece. And you might also want to fix up the BT_INTEL one since that has the same “problem”. If we fix something, then we fix it for all current existences.

And s/btusb/btbcm/ in case you overlooked that small copy&paste bug.

>
> config BT_HCIBTUSB
> tristate "HCI USB driver"
> @@ -22,15 +28,16 @@ config BT_HCIBTUSB
> kernel or say M to compile it as module (btusb).
>
> config BT_HCIBTUSB_BCM
> - bool "Broadcom protocol support"
> + bool "Broadcom USB support”

I preferred “protocol” over “USB” here when I added it for the simple reason since this is the Generic USB driver. No need to repeat the USB part of it. That is pretty obvious.

> depends on BT_HCIBTUSB
> select BT_BCM
> default y
> help
> - The Broadcom protocol support enables firmware and patchram
> - download support for Broadcom Bluetooth controllers.
> + Broadcom Bluetooth USB driver support.
> + The Broadcom USB support enables firmware and patchram
> + download for Broadcom Bluetooth USB controllers.
>
> - Say Y here to compile support for Broadcom protocol.
> + Say Y here to compile support for Broadcom USB.

I am not convinced that this makes it clearer. It is an option of the USB driver. It is not a different driver. It is still the same driver. It is just enabling the support for Broadcom based devices.

>
> config BT_HCIBTSDIO
> tristate "HCI SDIO driver"
> @@ -125,15 +132,25 @@ config BT_HCIUART_INTEL
> Say Y here to compile support for Intel protocol.
>
> config BT_HCIUART_BCM
> - bool "Broadcom protocol support"
> - depends on BT_HCIUART
> + bool "Broadcom BT UART serial support”

Same explanation as above. It is an option of the UART driver. No need to repeat that information. That is why I choose to use the word “protocol” in the first place.

> select BT_HCIUART_H4
> + select BT_UART_BCM
> select BT_BCM
> help
> - The Broadcom protocol support enables Bluetooth HCI over serial
> - port interface for Broadcom Bluetooth controllers.
> + HCI_UART_BCM is a protocol for initializing, managing and
> + communicating with Broadcom UART Bluetooth devices.
> + This protocol initializes chips and power-manages them.
> + Enable this if you have serial Broadcom Bluetooth device.
> +
> + Say Y here to compile support for Broadcom UART protocol.
> +
> +config BT_UART_BCM
> + tristate "Broadcom BT UART driver"
> + depends on BT_HCIUART_H4 && TTY
> + help
> + This driver supports the HCI_UART_BT protocol.
>
> - Say Y here to compile support for Broadcom protocol.
> + It manages Bluetooth UART device properties and GPIOs.

My idea was actually not putting this into a separate kernel module. So I have to read through this whole patch set to see if that makes sense or not. My initial thought has been that this is in the drivers/bluetooth/hci_bcm.c.

Just as a note, in case of a separate driver, that driver should have been in its own patch and not intermixed with this one. Mixing them together makes it really hard to review it.

>
> config BT_HCIBCM203X
> tristate "HCI BCM203x USB driver"
> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
> index dd0d9c4..0e5fd66 100644
> --- a/drivers/bluetooth/Makefile
> +++ b/drivers/bluetooth/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_BT_MRVL) += btmrvl.o
> obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
> obj-$(CONFIG_BT_WILINK) += btwilink.o
> obj-$(CONFIG_BT_BCM) += btbcm.o
> +obj-$(CONFIG_BT_UART_BCM) += btbcm_uart.o
>
> btmrvl-y := btmrvl_main.o
> btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
> diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
> index 4bba866..3e9ac30 100644
> --- a/drivers/bluetooth/btbcm.c
> +++ b/drivers/bluetooth/btbcm.c
> @@ -2,7 +2,8 @@
> *
> * Bluetooth support for Broadcom devices
> *
> - * Copyright (C) 2015 Intel Corporation
> + * Copyright (C) 2015 Intel Corporation
> + * Copyright (C) 2015 Broadcom Corporation

Just follow the double spaces that we normally do for separating the year from the company.

> *
> *
> * This program is free software; you can redistribute it and/or modify
> @@ -15,24 +16,22 @@
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - *

I know that eventually we have to remove the FSF address from the code, but that is clearly an individual cleanup patch that can come later. Mixing these in early on just adds clutter for the review.

> */
>
> #include <linux/module.h>
> #include <linux/firmware.h>
> +#include <linux/tty.h>
> #include <asm/unaligned.h>
>
> #include <net/bluetooth/bluetooth.h>
> #include <net/bluetooth/hci_core.h>
>
> +#include “hci_uart.h"

This screams layer violation.

btbcm.[ch] should be independent so that the USB and also the UART driver can utilise it.

Also changes to btbcm.[ch] should come as separate patches. They should come first and introduce new common used functionality.

> #include "btbcm.h"
>
> -#define VERSION "0.1"
> +#define VERSION "0.2"
>
> -#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
> +#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00} })

Remove this one. That whitespace fix you are doing here is wrong.

>
> int btbcm_check_bdaddr(struct hci_dev *hdev)
> {
> @@ -43,6 +42,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
> HCI_INIT_TIMEOUT);
> if (IS_ERR(skb)) {
> int err = PTR_ERR(skb);
> +

If you want to fix some coding style, then please do that in a separate patch. If they are valid fixes, then they are easy to apply. Intermixed with adding features it is a bad idea.

> BT_ERR("%s: BCM: Reading device address failed (%d)",
> hdev->name, err);
> return err;
> @@ -159,6 +159,8 @@ int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
> }
>
> /* 250 msec delay after Launch Ram completes */
> + BT_INFO("%s: BCM: Delaying upon the patch download completion...",
> + hdev->name);

Same on this one. If this adds value, please split this out and have a separate patch for it. I know this might sound a bit like extra overhead, but when having to look back at this in 2 years it helps a look to see the reasoning for it. So far we have gotten away with not printing this information and nobody complained. If this is just for debugging purposes, then keeping it as a separate patch is better. You can just drop it at the end.

> msleep(250);
>
> done:
> @@ -174,6 +176,7 @@ static int btbcm_reset(struct hci_dev *hdev)
> skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
> if (IS_ERR(skb)) {
> int err = PTR_ERR(skb);
> +
> BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err);
> return err;
> }
> @@ -246,8 +249,10 @@ static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
> static const struct {
> u16 subver;
> const char *name;
> + u32 baud_rate; /* operational baud rate */
> } bcm_uart_subver_table[] = {
> - { 0x410e, "BCM43341B0" }, /* 002.001.014 */
> + { 0x410e, "BCM43341B0", 3000000}, /* 002.001.014 */
> + { 0x610c, "BCM4354_003.001.012.0306.0659", 3000000}, /* 003.001.012 */

These are not firmware names. It is just suppose to the the first part of the string. So just adding firmware filenames here is the wrong approach.

And as we talked, soon, this needs to be replaced by a “manifest” file that will be loaded via request_firmware() from the firmware directory that allows to make the right selection of firmware file. We are just not there yet. So it will be easy to just add the new subver entry here as it is.

Also this table is not appropriate for the the operational baud rate. I prefer we do that exactly as Fred did in his patches and then maybe later add a feature to overwrite this via ACPI or DTS entries.

> { }
> };
>
> @@ -268,6 +273,133 @@ static const struct {
> { }
> };
>
> +/*
> + * Set the UART into the defaults
> + */
> +int btbcm_init_uart(struct hci_uart *hu)
> +{
> + struct ktermios ktermios;
> + struct tty_struct *tty = hu->tty;

As a general rule the variables that carry an assignment from some other struct come first.

> + int status, speed;
> +
> + /* Bring UART into default setting at 115200 */

I don’t think this comment belong here before flushing the buffer. I think here you want a comment that explain that we are flushing the line discipline buffers and the TTY buffers.

> + if (tty->ldisc->ops->flush_buffer)
> + tty->ldisc->ops->flush_buffer(tty);
> + tty_driver_flush_buffer(tty);

And this should have an extra space in between here and then add the comment to put the UART back into default mode. I would actually list all default options we are requiring and not just the speed.

> + ktermios = tty->termios;
> + BT_DBG("init_uart def flags c_o %x c_l %x c_c %x spd %d/%d",
> + ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
> + ktermios.c_ispeed, ktermios.c_ospeed);
> + ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
> + | INLCR | IGNCR | ICRNL | IXON);
> + ktermios.c_oflag &= ~OPOST;
> + ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
> + ktermios.c_cflag &= ~(CSIZE | PARENB | CBAUD);
> + ktermios.c_cflag |= CS8;
> + ktermios.c_cflag |= CRTSCTS;
> + ktermios.c_cflag |= B115200;
> + ktermios.c_ispeed = 115200;
> + ktermios.c_ospeed = 115200;
> + status = tty_set_termios(tty, &ktermios);

In general the variable name err is mostly used for this and not status.

> + if (status) {
> + BT_DBG("init_uart set_termios failure %d", status);
> + return status;
> + }
> +
> + speed = tty_get_baud_rate(tty);
> + BT_DBG("init_uart set_termios completed, spd %d", speed);

This warrants an extra empty line for easier readability.

> + ktermios = tty->termios;
> + BT_DBG("init_uart new flags c_o %x c_l %x c_c %x spd %d/%d",
> + ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
> + ktermios.c_ispeed, ktermios.c_ospeed);

However a lot of this pure debug code during development. Do you want to keep it around?

> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(btbcm_init_uart);

I wonder if this code actually belongs into btbcm module. It sounds like pretty generic code that could be just done in hci_ldisc.c and could be done beforehand. Mainly since all vendors have some sort of default baud rate and operational baud rate. We want to limit messing with the TTY code to a central location. I do not want to duplicate this code for Intel, Realtek or any other hardware manufacturer.

I think that Fred was doing that in a central location in hci_ldisc.c to have the HCI UART core handling prepare this.

> +
> +/*
> + * Set the baud rate on the UART and the device
> + */
> +int btbcm_set_baud_rate(struct hci_uart *hu, int baud_rate)
> +{

As I said above. This is a layer violation. struct hci_uart is internal to the hci_uart.ko driver. You can not expose this from the btbcm.ko driver.

> + struct ktermios ktermios;
> + struct tty_struct *tty = hu->tty;
> + int status, speed, cflag;
> + struct sk_buff *skb;
> + unsigned char enable = 1;
> + unsigned char baud_rate_vsc_pars[] = {0, 0, 0, 0x10, 0x0e, 0};

Might want to use u8 here instead. And 0x01 instead of 1 for these assignments.

> +
> + /* If the baud rate is higher than 3000000, change the clock */
> + if (baud_rate > 3000000) {
> + skb = __hci_cmd_sync(hu->hdev, 0xfc45, 1, &enable,
> + HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb)) {
> + status = PTR_ERR(skb);
> + return status;
> + }
> +
> + kfree_skb(skb);
> + BT_DBG("set_baud_rate write UART 48 MHz VSC succeeded");
> + }

I wonder if can always assume that the clock is set correctly and not better switch the clock back in case the value is lower.

Also is this now really enable or just a clock mode selection?

> +
> + /* Now let the device know about the rate change */
> + baud_rate_vsc_pars[2] = (unsigned char)(baud_rate & 0xff);
> + baud_rate_vsc_pars[3] = (unsigned char)((baud_rate >> 8) & 0xff);
> + baud_rate_vsc_pars[4] = (unsigned char)((baud_rate >> 16) & 0xff);
> + baud_rate_vsc_pars[5] = (unsigned char)((baud_rate >> 24) & 0xff);

We have function like put_unaligned_le32 for this. Or just define the vendor command struct for this.

> + skb = __hci_cmd_sync(hu->hdev, 0xfc18, sizeof(baud_rate_vsc_pars),
> + baud_rate_vsc_pars, HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb)) {
> + status = PTR_ERR(skb);
> + BT_ERR("set_baud_rate VSC failed (%d)", status);
> + return status;
> + }

The } is at the wrong indentation.

> +
> + kfree_skb(skb);
> + BT_DBG("set_baud_rate VSC succeeded");
> +
> + /* Set UART into this rate as well */
> + ktermios = tty->termios;
> + BT_DBG("set_baud_rate start flags c_o %x c_l %x c_c %x spd %d/%d",
> + ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
> + ktermios.c_ispeed, ktermios.c_ospeed);
> + switch (baud_rate) {
> + case 115200:
> + cflag |= B115200; break;
> + case 921600:
> + cflag |= B921600; break;
> + case 3000000:
> + cflag |= B3000000; break;
> + case 3500000:
> + cflag |= B3500000; break;
> + case 4000000:
> + cflag |= B4000000; break;
> + default:
> + BT_DBG("set_baud_rate unknown rate %d", baud_rate);
> + return -EINVAL;
> + }
> +
> + ktermios.c_cflag &= ~CBAUD;
> + ktermios.c_cflag |= cflag;
> + ktermios.c_ispeed = baud_rate;
> + ktermios.c_ospeed = baud_rate;
> + status = tty_set_termios(tty, &ktermios);
> + if (status) {
> + BT_DBG("set_baud_rate set_termios failure %d", status);
> + return status;
> + }
> +
> + speed = tty_get_baud_rate(tty);
> + BT_DBG("set_baud_rate set_termios completed, spd %d", speed);
> + ktermios = tty->termios;
> + BT_DBG("set_baud_rate flags c_o %x c_l %x c_c %x spd %d/%d",
> + ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
> + ktermios.c_ispeed, ktermios.c_ospeed);

Duplicating the actual TTY baud rate change code here makes it clear that we want that centralised in hci_ldisc.

> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(btbcm_set_baud_rate);
> +
> int btbcm_setup_patchram(struct hci_dev *hdev)
> {
> char fw_name[64];
> @@ -275,7 +407,8 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
> const char *hw_name = NULL;
> struct sk_buff *skb;
> struct hci_rp_read_local_version *ver;
> - int i, err;
> + int i, err, is_uart = false;
> + struct hci_uart *hu = hci_get_drvdata(hdev);
>
> /* Reset */
> err = btbcm_reset(hdev);
> @@ -297,14 +430,18 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
> if (IS_ERR(skb))
> return PTR_ERR(skb);
>
> - BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
> + BT_INFO("%s: BCM: chip id %u, rev 0x%x subver 0x%x",
> + hdev->name, skb->data[1], rev, subver);
> kfree_skb(skb);
>
> switch ((rev & 0xf000) >> 12) {
> case 0:
> + case 1:
> for (i = 0; bcm_uart_subver_table[i].name; i++) {
> if (subver == bcm_uart_subver_table[i].subver) {
> hw_name = bcm_uart_subver_table[i].name;
> + BT_INFO("UART firmware found: %s", hw_name);
> + is_uart = true;
> break;
> }
> }
> @@ -312,7 +449,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
> snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
> hw_name ? : "BCM");
> break;
> - case 1:
> +
> case 2:
> /* Read USB Product Info */
> skb = btbcm_read_usb_product(hdev);
> @@ -345,11 +482,25 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
> if (err == -ENOENT)
> return 0;
>
> + /* Once the patch is downloaded, the device is back at default rate */
> + if (is_uart) {
> + err = btbcm_init_uart(hu);
> + if (err)
> + return 0;
> + }
> +
> /* Reset */
> err = btbcm_reset(hdev);
> if (err)
> return err;
>
> + if (is_uart) {
> + err = btbcm_set_baud_rate(hu,
> + bcm_uart_subver_table[i].baud_rate);
> + if (err)
> + return 0;
> + }
> +

The is_uart decision is something that the driver should make. So while right now it is easy to just reference the setup function from the common code, I think the reality is that each driver (USB and UART) has to provide its own ->setup function for Broadcom devices and then stick the needed pieces together. So lets try to figure out what needs to be done when and what can be generic in hci_ldisc.c and then we move pieces into the right location.

> /* Read Local Version Info */
> skb = btbcm_read_local_version(hdev);
> if (IS_ERR(skb))
> diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
> index eb6ab5f..f7d30c6 100644
> --- a/drivers/bluetooth/btbcm.h
> +++ b/drivers/bluetooth/btbcm.h
> @@ -2,7 +2,8 @@
> *
> * Bluetooth support for Broadcom devices
> *
> - * Copyright (C) 2015 Intel Corporation
> + * Copyright (C) 2015 Intel Corporation
> + * Copyright (C) 2015 Broadcom Corporation
> *
> *
> * This program is free software; you can redistribute it and/or modify
> @@ -15,10 +16,6 @@
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - *
> */
>
> #if IS_ENABLED(CONFIG_BT_BCM)
> @@ -30,6 +27,8 @@ int btbcm_patchram(struct hci_dev *hdev, const char *firmware);
> int btbcm_setup_patchram(struct hci_dev *hdev);
> int btbcm_setup_apple(struct hci_dev *hdev);
>
> +int btbcm_init_uart(struct hci_uart *hu);
> +int btbcm_set_baud_rate(struct hci_uart *hu, int baud_rate);
> #else
>
> static inline int btbcm_check_bdaddr(struct hci_dev *hdev)
> @@ -57,4 +56,14 @@ static inline int btbcm_setup_apple(struct hci_dev *hdev)
> return 0;
> }
>
> +static int btbcm_init_uart(void *hu)

These should be static inline. Not that we can keep them this way, but just for future reference.

> +{
> + return 0;
> +}
> +
> +static int btbcm_set_baud_rate(void *hu, int baud_rate);
> +{
> + return 0;
> +}
> +
> #endif
> diff --git a/drivers/bluetooth/btbcm_uart.c b/drivers/bluetooth/btbcm_uart.c
> new file mode 100644
> index 0000000..3308bdb
> --- /dev/null
> +++ b/drivers/bluetooth/btbcm_uart.c
> @@ -0,0 +1,679 @@
> +/*
> + *
> + * Bluetooth BCM UART Driver
> + *
> + * Copyright (c) 2015 Broadcom Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/fcntl.h>
> +#include <linux/interrupt.h>
> +#include <linux/ptrace.h>
> +#include <linux/poll.h>
> +
> +#include <linux/slab.h>
> +#include <linux/tty.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/signal.h>
> +#include <linux/ioctl.h>
> +#include <linux/skbuff.h>
> +#include <linux/list.h>
> +
> +#include <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +
> +#include <linux/gpio/consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_platform.h>
> +
> +#include "btbcm_uart.h"
> +
> +static int idleTimeout = 5;

We do not name variables in camel case. This would be idle_timeout.

> +module_param(idleTimeout, int, 0);
> +MODULE_PARM_DESC(idleTimeout, "Bluetooth idle timeout in seconds");
> +
> +/* Device context */
> +struct bcm_device {
> + struct list_head list;
> +
> + struct platform_device *pdev;
> + struct gpio_desc *bt_wake_gpio;
> + struct gpio_desc *dev_wake_gpio;
> + struct gpio_desc *reg_on_gpio;
> + int bt_wake_irq;
> + int dev_wake_active_low;
> + int reg_on_active_low;
> + int bt_wake_active_low;
> + u32 configure_sleep;
> + u32 manual_fc;
> + u32 baud_rate_before_config_download;
> + u32 configure_audio;
> + u32 PCMClockMode;
> + u32 PCMFillMethod;
> + u32 PCMFillNum;
> + u32 PCMFillValue;
> + u32 PCMInCallBitclock;
> + u32 PCMLSBFirst;
> + u32 PCMRightJustify;
> + u32 PCMRouting;
> + u32 PCMShortFrameSync;
> + u32 PCMSyncMode;
> +
> + char tty_name[64];
> +
> + struct btbcm_uart_callbacks protocol_callbacks;
> + struct work_struct wakeup_work;
> +};
> +
> +/* List of BCM BT UART devices */
> +static DEFINE_SPINLOCK(device_list_lock);
> +static LIST_HEAD(device_list);
> +
> +/*
> + * Calling the BCM protocol at lower execution priority
> + */
> +static void bcm_bt_wakeup_task(struct work_struct *ws)
> +{
> + int resume_flag;
> + struct bcm_device *p_bcm_device =
> + container_of(ws, struct bcm_device, wakeup_work);
> +
> + if (!p_bcm_device) {
> + BT_DBG("bcm_bt_wakeup_task - failing, no device");
> + return;
> + }
> +
> + /* Make sure the device is resumed */
> + resume_flag = !p_bcm_device->dev_wake_active_low;
> + if (p_bcm_device->dev_wake_gpio) {
> + gpiod_set_value(p_bcm_device->dev_wake_gpio, resume_flag);
> + BT_DBG("bcm_bt_wakeup_task - resume %d written, delaying 15 ms",
> + resume_flag);
> + mdelay(15);
> + }
> +
> + /* Let the protocol know it's time to wake up */
> + if (p_bcm_device->protocol_callbacks.p_wakeup)
> + p_bcm_device->protocol_callbacks.p_wakeup(
> + p_bcm_device->protocol_callbacks.context);
> +}
> +
> +/*
> + * Interrupt routine for the wake from the device
> + */
> +static irqreturn_t bcm_bt_uart_isr(int irq, void *context)
> +{
> + unsigned int bt_wake;
> + struct bcm_device *p = (struct bcm_device *)context;
> +
> + bt_wake = gpiod_get_value(p->bt_wake_gpio);
> + BT_DBG("bcm_bt_uart_isr with bt_wake of %d (active_low %d), req bh",
> + bt_wake, p->bt_wake_active_low);
> +
> + /* Defer the actual processing to the platform work queue */
> + schedule_work(&p->wakeup_work);
> + return IRQ_HANDLED;
> +}
> +
> +/*
> + * Device instance startup
> + */
> +static int bcm_bt_uart_probe(struct platform_device *pdev)
> +{
> + int ret = 0;
> + struct device_node *np = pdev->dev.of_node;
> + const char *tty_name;
> + struct bcm_device *p_bcm_device = NULL;
> +
> + p_bcm_device = devm_kzalloc(&pdev->dev, sizeof(*p_bcm_device),
> + GFP_KERNEL);
> + if (!p_bcm_device) {
> + BT_DBG("bcm_bt_uart_probe - failing due to no memory");
> + return -ENOMEM;
> + }
> + p_bcm_device->pdev = pdev;
> + BT_DBG("bcm_bt_uart_probe %p context", p_bcm_device);
> +
> + /* Get dev wake GPIO */
> + p_bcm_device->dev_wake_gpio = gpiod_get(&pdev->dev, "bt-wake");
> + BT_DBG("bcm_bt_uart_probe - gpiod_get for bt-wake returned %p",
> + p_bcm_device->dev_wake_gpio);
> + if (IS_ERR(p_bcm_device->dev_wake_gpio)) {
> + ret = PTR_ERR(p_bcm_device->dev_wake_gpio);
> + if (ret != -ENOENT) {
> + dev_err(&pdev->dev,
> + "bcm_bt_uart_probe - dev_wake GPIO: %d\n", ret);
> + }
> + p_bcm_device->dev_wake_gpio = NULL;
> + } else {
> + int resume_flag;
> +
> + p_bcm_device->dev_wake_active_low = gpiod_is_active_low
> + (p_bcm_device->dev_wake_gpio);
> + BT_DBG("bcm_bt_uart_probe - dev_wake a-low is %d (cans %d)",
> + p_bcm_device->dev_wake_active_low,
> + gpiod_cansleep(p_bcm_device->dev_wake_gpio));
> +
> + /* configure dev_wake as output with init resumed state */
> + resume_flag = !p_bcm_device->dev_wake_active_low;
> + ret = gpiod_direction_output(p_bcm_device->dev_wake_gpio,
> + resume_flag);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "bcm_bt_uart_probe s dev_wake GPIO: %d\n", ret);
> + gpiod_put(p_bcm_device->dev_wake_gpio);
> + p_bcm_device->dev_wake_gpio = NULL;
> + goto end;
> + } else {
> + BT_DBG("bcm_bt_uart_probe - dev_wake set to %d",
> + resume_flag);
> + }
> + }
> +
> + /* Get power on/off GPIO */
> + p_bcm_device->reg_on_gpio = gpiod_get(&pdev->dev, "reg-on");
> + BT_DBG("bcm_bt_uart_probe - gpiod_get for reg-on returned %p",
> + p_bcm_device->reg_on_gpio);
> + if (IS_ERR(p_bcm_device->reg_on_gpio)) {
> + ret = PTR_ERR(p_bcm_device->reg_on_gpio);
> + if (ret != -ENOENT) {
> + dev_err(&pdev->dev,
> + "bcm_bt_uart_probe - reg_on GPIO: %d\n", ret);
> + }
> + p_bcm_device->reg_on_gpio = NULL;
> + } else {
> + int poweron_flag;
> +
> + p_bcm_device->reg_on_active_low = gpiod_is_active_low
> + (p_bcm_device->reg_on_gpio);
> + BT_DBG("bcm_bt_uart_probe - reg_on a-low is %d (cans %d)",
> + p_bcm_device->reg_on_active_low,
> + gpiod_cansleep(p_bcm_device->reg_on_gpio));
> +
> + /* configure reg_on as output with init on state */
> + poweron_flag = !p_bcm_device->reg_on_active_low;
> + ret = gpiod_direction_output(p_bcm_device->reg_on_gpio,
> + poweron_flag);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "bcm_bt_uart_probe s reg_on GPIO: %d\n", ret);
> + gpiod_put(p_bcm_device->reg_on_gpio);
> + p_bcm_device->reg_on_gpio = NULL;
> + } else {
> + BT_DBG("bcm_bt_uart_probe - reg_on initially set to %d",
> + poweron_flag);
> + }
> + }
> +
> + platform_set_drvdata(pdev, p_bcm_device);
> + /* Must be done before interrupt is requested */
> + INIT_WORK(&p_bcm_device->wakeup_work, bcm_bt_wakeup_task);
> +
> + /* Get bt host wake GPIO */
> + p_bcm_device->bt_wake_gpio = gpiod_get(&pdev->dev, "bt-host-wake");
> + BT_DBG("bcm_bt_uart_probe - gpiod_get for bt-host-wake returned %p",
> + p_bcm_device->bt_wake_gpio);
> + if (IS_ERR(p_bcm_device->bt_wake_gpio)) {
> + ret = PTR_ERR(p_bcm_device->bt_wake_gpio);
> + if (ret != -ENOENT) {
> + dev_err(&pdev->dev,
> + "bcm_bt_uart_probe - bt_wake GPIO: %d\n", ret);
> + }
> + p_bcm_device->bt_wake_gpio = NULL;
> + } else {
> + /* configure bt_wake as input */
> + ret = gpiod_direction_input(p_bcm_device->bt_wake_gpio);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "bcm_bt_uart_probe s bt_wake GPIO: %d\n", ret);
> + gpiod_put(p_bcm_device->bt_wake_gpio);
> + p_bcm_device->bt_wake_gpio = NULL;
> + } else {
> + p_bcm_device->bt_wake_active_low = gpiod_is_active_low
> + (p_bcm_device->bt_wake_gpio);
> + BT_DBG("bcm_bt_uart_probe -bt_wake a-low is %d(cans%d)",
> + p_bcm_device->bt_wake_active_low,
> + gpiod_cansleep(p_bcm_device->bt_wake_gpio));
> + p_bcm_device->bt_wake_irq = gpiod_to_irq
> + (p_bcm_device->bt_wake_gpio);
> + if (p_bcm_device->bt_wake_irq < 0) {
> + dev_err(&pdev->dev,
> + "bcm_bt_uart_probe - HOST_WAKE IRQ: %d\n", ret);
> + } else {
> + unsigned long intflags = IRQF_TRIGGER_RISING;
> +
> + if (p_bcm_device->bt_wake_active_low)
> + intflags = IRQF_TRIGGER_FALLING;
> +
> + ret = request_irq(p_bcm_device->bt_wake_irq,
> + bcm_bt_uart_isr,
> + intflags, "bt_host_wake",
> + p_bcm_device);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "bcm_bt_uart_probe - failed to conf IRQ %d: %d",
> + p_bcm_device->bt_wake_irq, ret);
> + } else {
> + BT_DBG("bcm_bt_uart_probe - IRQ %d",
> + p_bcm_device->bt_wake_irq);
> + }
> + }
> + }
> + }
> +
> + p_bcm_device->configure_sleep = 0;
> + if (!of_property_read_u32(np, "configure-sleep",
> + &p_bcm_device->configure_sleep)) {
> + BT_DBG("configure-sleep read as %d",
> + p_bcm_device->configure_sleep);
> + }
> + p_bcm_device->manual_fc = 0;
> + if (!of_property_read_u32(np, "manual-fc",
> + &p_bcm_device->manual_fc)) {
> + BT_DBG("manual-fc read as %d",
> + p_bcm_device->manual_fc);
> + }
> + p_bcm_device->baud_rate_before_config_download = 3000000;
> + if (!of_property_read_u32(
> + np, "baud-rate-before-config-download",
> + &p_bcm_device->baud_rate_before_config_download)) {
> + BT_DBG("baud-rate-before-config-download read as %d",
> + p_bcm_device->baud_rate_before_config_download);
> + }
> + p_bcm_device->configure_audio = 0;
> + if (!of_property_read_u32(np, "configure-audio",
> + &p_bcm_device->configure_audio)) {
> + BT_DBG("configure-audio read as %d",
> + p_bcm_device->configure_audio);
> + }
> + if (p_bcm_device->configure_audio) {
> + /* Defaults for audio */
> + p_bcm_device->PCMClockMode = 0;
> + p_bcm_device->PCMFillMethod = 2;
> + p_bcm_device->PCMFillNum = 0;
> + p_bcm_device->PCMFillValue = 3;
> + p_bcm_device->PCMInCallBitclock = 0;
> + p_bcm_device->PCMLSBFirst = 0;
> + p_bcm_device->PCMRightJustify = 0;
> + p_bcm_device->PCMRouting = 0;
> + p_bcm_device->PCMShortFrameSync = 0;
> + p_bcm_device->PCMSyncMode = 0;
> +
> + if (!of_property_read_u32(np, "PCMClockMode",
> + &p_bcm_device->PCMClockMode))
> + BT_DBG("PCMClockMode read as %d",
> + p_bcm_device->PCMClockMode);
> + if (!of_property_read_u32(np, "PCMFillMethod",
> + &p_bcm_device->PCMFillMethod))
> + BT_DBG("PCMFillMethod readas %d",
> + p_bcm_device->PCMFillMethod);
> + if (!of_property_read_u32(np, "PCMFillNum",
> + &p_bcm_device->PCMFillNum))
> + BT_DBG("PCMFillNum read as %d",
> + p_bcm_device->PCMFillNum);
> + if (!of_property_read_u32(np, "PCMFillValue",
> + &p_bcm_device->PCMFillValue))
> + BT_DBG("PCMFillValue read as %d",
> + p_bcm_device->PCMFillValue);
> + if (!of_property_read_u32(np, "PCMInCallBitclock",
> + &p_bcm_device->PCMInCallBitclock))
> + BT_DBG("PCMInCallBitclock read as %d",
> + p_bcm_device->PCMInCallBitclock);
> + if (!of_property_read_u32(np, "PCMLSBFirst",
> + &p_bcm_device->PCMLSBFirst))
> + BT_DBG("PCMLSBFirst read as %d",
> + p_bcm_device->PCMLSBFirst);
> + if (!of_property_read_u32(np, "PCMRightJustify",
> + &p_bcm_device->PCMRightJustify))
> + BT_DBG("PCMRightJustify read as %d",
> + p_bcm_device->PCMRightJustify);
> + if (!of_property_read_u32(np, "PCMRouting",
> + &p_bcm_device->PCMRouting))
> + BT_DBG("PCMRouting read as %d",
> + p_bcm_device->PCMRouting);
> + if (!of_property_read_u32(np, "PCMShortFrameSync",
> + &p_bcm_device->PCMShortFrameSync))
> + BT_DBG("PCMShortFrameSync read as %d",
> + p_bcm_device->PCMShortFrameSync);
> + if (!of_property_read_u32(np, "PCMSyncMode",
> + &p_bcm_device->PCMSyncMode))
> + BT_DBG("PCMSyncMode read as %d",
> + p_bcm_device->PCMSyncMode);
> + }
> +
> + if (!of_property_read_string(np, "tty", &tty_name)) {
> + strcpy(p_bcm_device->tty_name, tty_name);
> + BT_DBG("tty name read as %s", p_bcm_device->tty_name);
> + }
> +
> + BT_DBG("idleTimeout set as %d", idleTimeout);
> +
> + ret = 0; /* If we made it here, we're fine */
> +
> + /* Place this instance on the device list */
> + spin_lock(&device_list_lock);
> + list_add_tail(&p_bcm_device->list, &device_list);
> + spin_unlock(&device_list_lock);
> +
> +end:
> + if (ret) {
> + if (p_bcm_device->reg_on_gpio) {
> + gpiod_put(p_bcm_device->reg_on_gpio);
> + p_bcm_device->reg_on_gpio = NULL;
> + }
> + if (p_bcm_device->bt_wake_gpio) {
> + gpiod_put(p_bcm_device->bt_wake_gpio);
> + p_bcm_device->bt_wake_gpio = NULL;
> + }
> + if (p_bcm_device->dev_wake_gpio) {
> + gpiod_put(p_bcm_device->dev_wake_gpio);
> + p_bcm_device->dev_wake_gpio = NULL;
> + }
> + }
> +
> + BT_DBG("bcm_bt_uart_probe with the result %d", ret);
> + return ret;
> +}
> +
> +/*
> + * Device instance removal
> + */
> +static int bcm_bt_uart_remove(struct platform_device *pdev)
> +{
> + struct bcm_device *p_bcm_device = platform_get_drvdata(pdev);
> +
> + if (p_bcm_device == NULL) {
> + BT_DBG("bcm_bt_uart_remove - logic error, no probe?!");
> + return 0;
> + }
> +
> + BT_DBG("bcm_bt_uart_remove %p context", p_bcm_device);
> +
> + spin_lock(&device_list_lock);
> + list_del(&p_bcm_device->list);
> + spin_unlock(&device_list_lock);
> +
> + BT_DBG("bcm_bt_uart_remove - freeing interrupt %d",
> + p_bcm_device->bt_wake_irq);
> + free_irq(p_bcm_device->bt_wake_irq, p_bcm_device);
> +
> + if (p_bcm_device->reg_on_gpio) {
> + BT_DBG("bcm_bt_uart_remove - releasing reg_on_gpio");
> + gpiod_put(p_bcm_device->reg_on_gpio);
> + p_bcm_device->reg_on_gpio = NULL;
> + }
> +
> + if (p_bcm_device->dev_wake_gpio) {
> + BT_DBG("bcm_bt_uart_remove - releasing dev_wake_gpio");
> + gpiod_put(p_bcm_device->dev_wake_gpio);
> + p_bcm_device->dev_wake_gpio = NULL;
> + }
> +
> + if (p_bcm_device->bt_wake_gpio) {
> + BT_DBG("bcm_bt_uart_remove - releasing bt_wake_gpio");
> + gpiod_put(p_bcm_device->bt_wake_gpio);
> + p_bcm_device->bt_wake_gpio = NULL;
> + }
> +
> + BT_DBG("bcm_bt_uart_remove %p done", p_bcm_device);
> + return 0;
> +}
> +
> +/*
> + * Platform resume callback
> + */
> +static int bcm_bt_uart_resume(struct device *pdev)
> +{
> + int resume_flag;
> + struct bcm_device *p_bcm_device = platform_get_drvdata(
> + to_platform_device(pdev));
> +
> + if (p_bcm_device == NULL) {
> + BT_DBG("bcm_bt_uart_resume - logic error, no device?!");
> + return 0;
> + }
> +
> + BT_DBG("bcm_bt_uart_resume %p", p_bcm_device);
> +
> + resume_flag = !p_bcm_device->dev_wake_active_low;
> + if (p_bcm_device->dev_wake_gpio) {
> + gpiod_set_value(p_bcm_device->dev_wake_gpio, resume_flag);
> + BT_DBG("bcm_bt_uart_resume: %d written, delaying 15 ms",
> + resume_flag);
> + mdelay(15);
> + }
> +
> + /* Let the protocol know the platform is resuming */
> + if (p_bcm_device->protocol_callbacks.p_resume)
> + p_bcm_device->protocol_callbacks.p_resume(
> + p_bcm_device->protocol_callbacks.context);
> +
> + return 0;
> +}
> +
> +/*
> + * Platform suspend callback
> + */
> +static int bcm_bt_uart_suspend(struct device *pdev)
> +{
> + int resume_flag;
> + struct bcm_device *p_bcm_device = platform_get_drvdata(
> + to_platform_device(pdev));
> +
> + if (p_bcm_device == NULL) {
> + BT_DBG("bcm_bt_uart_suspend - logic error, no device?!");
> + return 0;
> + }
> +
> + BT_DBG("bcm_bt_uart_suspend %p", p_bcm_device);
> +
> + /* Let the protocol know the platform is suspending */
> + if (p_bcm_device->protocol_callbacks.p_suspend)
> + p_bcm_device->protocol_callbacks.p_suspend(
> + p_bcm_device->protocol_callbacks.context);
> +
> + /* Suspend the device */
> + if (p_bcm_device->dev_wake_gpio) {
> + resume_flag = !p_bcm_device->dev_wake_active_low;
> + gpiod_set_value(p_bcm_device->dev_wake_gpio, !resume_flag);
> + BT_DBG("bcm_bt_uart_suspend: %d written, delaying 15 ms",
> + !resume_flag);
> + mdelay(15);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Entry point for calls from the protocol
> + */
> +int btbcm_uart_control(int action, void *device_context,
> + void *p_data, unsigned long *p_size)
> +{
> + struct btbcm_uart_callbacks *pc;
> + struct btbcm_uart_parameters *pp = p_data; /* for pars action only */
> + int ret = 0;
> + int resume_flag, poweron_flag;

For true boolean variable, please use bool instead of int.

> + struct bcm_device *p_bcm_device = device_context;
> + struct list_head *ptr;
> + bool is_found = false;
> +
> + /* Special processing for the callback configuration */
> + if (action == BTBCM_UART_ACTION_CONFIGURE_CALLBACKS) {
> + pc = p_data;
> +
> + BT_DBG("btbcm_uart_control - configure callbacks");
> + if ((p_data == NULL) || *p_size != sizeof(struct

Extra () around x == y are not needed. Do bother with them.

> + btbcm_uart_callbacks) || (pc->interface_version !=
> + BTBCM_UART_INTERFACE_VERSION)) {

Wrong indentation.

> + BT_DBG("btbcm_uart_control - callbacks mismatch!");
> + return -E2BIG;
> + }
> +
> + BT_DBG("btbcm_uart_control - configure callbacks for %s(%p)",
> + pc->name, pc->context);
> + if (p_bcm_device == NULL) {
> + spin_lock(&device_list_lock);
> + list_for_each(ptr, &device_list) {
> + p_bcm_device = list_entry(ptr, struct
> + bcm_device, list);
> + if (!strcmp(p_bcm_device->tty_name, pc->name)) {
> + is_found = true;
> + break;
> + }
> + }
> +
> + spin_unlock(&device_list_lock);
> + if (!is_found) {
> + BT_DBG("btbcm_uart_control - no device!");
> + return -ENOENT;
> + }
> + }
> +
> + p_bcm_device->protocol_callbacks = *pc;
> + memcpy(p_data, &p_bcm_device, sizeof(p_bcm_device));
> + *p_size = sizeof(p_bcm_device);
> + return ret;
> + }
> +
> + /* All other requests must have the right context */
> + if (p_bcm_device == NULL) {
> + BT_DBG("btbcm_uart_control - failing, no device");
> + return -ENOENT;
> + }
> +
> + switch (action) {
> + case BTBCM_UART_ACTION_POWER_ON:
> + BT_DBG("btbcm_uart_control %p - power on", device_context);
> + if (p_bcm_device->reg_on_gpio) {
> + poweron_flag = !p_bcm_device->reg_on_active_low;
> + gpiod_set_value(p_bcm_device->reg_on_gpio,
> + poweron_flag);
> + BT_DBG("btbcm_uart_control - pwron %d, delay 15 ms",
> + poweron_flag);
> + mdelay(15);
> + }
> + break;
> +
> + case BTBCM_UART_ACTION_POWER_OFF:
> + BT_DBG("btbcm_uart_control %p - power off", device_context);
> + if (p_bcm_device->reg_on_gpio) {
> + poweron_flag = p_bcm_device->reg_on_active_low;
> + gpiod_set_value(p_bcm_device->reg_on_gpio,
> + poweron_flag);
> + BT_DBG("btbcm_uart_control - pwroff %d, delay 15 ms",
> + poweron_flag);
> + mdelay(15);
> + }
> + break;
> +
> + case BTBCM_UART_ACTION_RESUME:
> + BT_DBG("btbcm_uart_control %p - resume", device_context);
> + if (p_bcm_device->dev_wake_gpio) {
> + resume_flag = !p_bcm_device->dev_wake_active_low;
> + gpiod_set_value(p_bcm_device->dev_wake_gpio,
> + resume_flag);
> + BT_DBG("btbcm_uart_control - resume %d, delay 15 ms",
> + resume_flag);
> + mdelay(15);
> + }
> + break;
> +
> + case BTBCM_UART_ACTION_SUSPEND:
> + BT_DBG("btbcm_uart_control %p - suspend", device_context);
> + if (p_bcm_device->dev_wake_gpio) {
> + resume_flag = !p_bcm_device->dev_wake_active_low;
> + gpiod_set_value(p_bcm_device->dev_wake_gpio,
> + !resume_flag);
> + BT_DBG("btbcm_uart_control - suspend %d, delay 15ms",
> + !resume_flag);
> + mdelay(15);
> + }
> + break;
> +
> + case BTBCM_UART_ACTION_GET_PARAMETERS:
> + BT_DBG("btbcm_uart_control %p - get pars", device_context);
> + if ((p_data == NULL) ||
> + (*p_size < sizeof(struct btbcm_uart_parameters))) {
> + BT_DBG("btbcm_uart_control - failing, wrong par size");
> + return -E2BIG;
> + }
> +
> + memset(pp, 0, sizeof(struct btbcm_uart_parameters));
> + pp->interface_version = BTBCM_UART_INTERFACE_VERSION;
> + pp->configure_sleep = p_bcm_device->configure_sleep;
> + pp->manual_fc = p_bcm_device->manual_fc;
> + pp->dev_wake_active_low = p_bcm_device->dev_wake_active_low;
> + pp->bt_wake_active_low = p_bcm_device->bt_wake_active_low;
> + pp->idle_timeout_in_secs = idleTimeout;
> + pp->baud_rate_before_config_download =
> + p_bcm_device->baud_rate_before_config_download;
> + pp->configure_audio = p_bcm_device->configure_audio;
> + pp->PCMClockMode = p_bcm_device->PCMClockMode;
> + pp->PCMFillMethod = p_bcm_device->PCMFillMethod;
> + pp->PCMFillNum = p_bcm_device->PCMFillNum;
> + pp->PCMFillValue = p_bcm_device->PCMFillValue;
> + pp->PCMInCallBitclock = p_bcm_device->PCMInCallBitclock;
> + pp->PCMLSBFirst = p_bcm_device->PCMLSBFirst;
> + pp->PCMRightJustify = p_bcm_device->PCMRightJustify;
> + pp->PCMRouting = p_bcm_device->PCMRouting;
> + pp->PCMShortFrameSync = p_bcm_device->PCMShortFrameSync;
> + pp->PCMSyncMode = p_bcm_device->PCMSyncMode;
> + *p_size = sizeof(struct btbcm_uart_parameters);
> + break;
> +
> + default:
> + BT_DBG("btbcm_uart_control %p unknown act %d",
> + device_context, action);
> + ret = -EINVAL;
> + break;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(btbcm_uart_control);
> +
> +/* Platform susp and resume callbacks */
> +static SIMPLE_DEV_PM_OPS(bcm_bt_uart_pm_ops,
> + bcm_bt_uart_suspend, bcm_bt_uart_resume);
> +
> +/* Driver match table */
> +static const struct of_device_id bcm_bt_uart_match_table[] = {

No need to call it match_table. Just table as suffice is fine.

> + { .compatible = "brcm,brcm-bt-uart" },
> + {}
> +};
> +
> +/* Driver configuration */
> +static struct platform_driver bcm_bt_uart_platform_driver = {

No need to call it platform_driver. Just driver as suffix is fine.

> + .probe = bcm_bt_uart_probe,
> + .remove = bcm_bt_uart_remove,
> + .driver = {
> + .name = "brcm_bt_uart",
> + .of_match_table = of_match_ptr(bcm_bt_uart_match_table),
> + .owner = THIS_MODULE,
> + .pm = &bcm_bt_uart_pm_ops,
> + },
> +};
> +
> +module_platform_driver(bcm_bt_uart_platform_driver);
> +
> +MODULE_AUTHOR("Ilya Faenson");
> +MODULE_DESCRIPTION("Broadcom Bluetooth UART Driver");
> +MODULE_LICENSE("Dual BSD/GPL”);

Copyright header says GPLv2. So lets reflect the correct license here.

> +
> diff --git a/drivers/bluetooth/btbcm_uart.h b/drivers/bluetooth/btbcm_uart.h
> new file mode 100644
> index 0000000..5801753
> --- /dev/null
> +++ b/drivers/bluetooth/btbcm_uart.h
> @@ -0,0 +1,90 @@
> +/*
> + *
> + * Bluetooth BCM UART Driver Header
> + *
> + * Copyright (c) 2015 Broadcom Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +#ifndef BTBCM_UART_H
> +#define BTBCM_UART_H
> +
> +/* Change the version if you change anything in this header */
> +#define BTBCM_UART_INTERFACE_VERSION 1

I do not see the need for this. Why would we do that?

> +
> +/* Callbacks from the driver into the protocol */
> +typedef void (*p_suspend_callback)(void *context);
> +typedef void (*p_resume_callback)(void *context);
> +typedef void (*p_wakeup_callback)(void *context);

Extra empty line here.

> +struct btbcm_uart_callbacks {
> + int interface_version; /* interface # compiled against */
> + void *context; /* protocol instance context */
> + char name[64]; /* protocol tty device, for example, ttyS0 */

Why would we record the TTY name. It might actually change.

> +
> + /* Callbacks proper */
> + p_suspend_callback p_suspend;
> + p_resume_callback p_resume;
> + p_wakeup_callback p_wakeup;

I do not get this p_ prefix naming. What is that for?

> +};
> +
> +/* Driver parameters retrieved from the DT or ACPI */
> +struct btbcm_uart_parameters {
> + int interface_version; /* interface # compiled against */
> +
> + /* Parameters proper */

What is a “proper”?

> + int configure_sleep;
> + int manual_fc;
> + int dev_wake_active_low;
> + int bt_wake_active_low;
> + int idle_timeout_in_secs;
> + int baud_rate_before_config_download;
> + int configure_audio;
> + int PCMClockMode;
> + int PCMFillMethod;
> + int PCMFillNum;
> + int PCMFillValue;
> + int PCMInCallBitclock;
> + int PCMLSBFirst;
> + int PCMRightJustify;
> + int PCMRouting;
> + int PCMShortFrameSync;
> + int PCMSyncMode;
> +};
> +
> +/*
> + * Actions on the BTBCM_UART driver
> + */
> +
> +/* Configure protocol callbacks */
> +#define BTBCM_UART_ACTION_CONFIGURE_CALLBACKS 0
> +
> +/* Retrieve BT device parameters */
> +#define BTBCM_UART_ACTION_GET_PARAMETERS 1
> +
> +/* Resume the BT device via GPIO */
> +#define BTBCM_UART_ACTION_RESUME 2
> +
> +/* Suspend the BT device via GPIO */
> +#define BTBCM_UART_ACTION_SUSPEND 3
> +
> +/* Power the BT device off via GPIO */
> +#define BTBCM_UART_ACTION_POWER_OFF 4
> +
> +/* Power the BT device on via GPIO */
> +#define BTBCM_UART_ACTION_POWER_ON 5
> +
> +/* Execute an action on the BT device */
> +extern int btbcm_uart_control(int action, void *device_context,
> + void *p_data, unsigned long *p_size);

Can these be 6 individual function instead of trying to fiddle this through a single one?

> +
> +#endif
> +
> diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
> index 1ec0b4a..e70f89b 100644
> --- a/drivers/bluetooth/hci_bcm.c
> +++ b/drivers/bluetooth/hci_bcm.c
> @@ -1,8 +1,13 @@
> /*
> *
> - * Bluetooth HCI UART driver for Broadcom devices
> + * Bluetooth UART H4 protocol for Broadcom devices
> *
> - * Copyright (C) 2015 Intel Corporation
> + * Copyright (c) 2015 Intel Corporation
> + * Copyright (c) 2015 Broadcom Corporation
> + *
> + * Acknowledgements:
> + * This file has been based on hci_h4.c originally developed
> + * by Maxim Krasnyansky and Marcel Holtmann.
> *
> *
> * This program is free software; you can redistribute it and/or modify
> @@ -15,139 +20,842 @@
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - *
> */
>
> +#include <linux/module.h>
> +
> #include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/fcntl.h>
> +#include <linux/interrupt.h>
> +#include <linux/ptrace.h>
> +#include <linux/poll.h>
> +
> +#include <linux/slab.h>
> +#include <linux/tty.h>
> #include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/signal.h>
> +#include <linux/ioctl.h>
> #include <linux/skbuff.h>
>
> #include <net/bluetooth/bluetooth.h>
> #include <net/bluetooth/hci_core.h>
>
> -#include "btbcm.h"
> +#include <linux/gpio/consumer.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_platform.h>
> +
> #include "hci_uart.h"
> +#include "btbcm.h"
> +#include "btbcm_uart.h"
>
> -struct bcm_data {
> - struct sk_buff *rx_skb;
> +/* Protocol context */
> +struct bcm_h4_struct {


I am missing the reason for this renaming.

> struct sk_buff_head txq;
> + struct hci_uart *hu;
> +
> + bool is_suspended; /* suspend/resume flag */
> +
> + /* Recv data parsing is not used in normal operation */
> + bool parse_recv;
> + /* buffer includes the type */
> + unsigned char reassembly[1 + HCI_MAX_FRAME_SIZE];
> + unsigned int rsize;
> + u16 rexpected;

So the nice H:4 RX helper function that I build is not good enough? Really? You need to really make a good case for not using it.

> +
> + struct timer_list timer; /* idle timer */
> +
> + struct btbcm_uart_parameters pars; /* device parameters */
> + void *device_context; /* ACPI/DT device context */
> };
>
> -static int bcm_open(struct hci_uart *hu)
> +/* Static function prototypes for forward references */
> +static void suspend_notification(void *context);
> +static void resume_notification(void *context);
> +static void wakeup_notification(void *context);
> +static void bcm_ensure_wakeup(struct hci_uart *hu);

I dislike forward declaration. If they can be avoid, lets avoid them and the function into the better location.

> +
> +/* Suspend/resume synchronization mutex */
> +static DEFINE_MUTEX(plock);
> +
> +/*
> + * Idle timer callback
> + */
> +static void bcm_idle_timeout(unsigned long arg)
> {
> - struct bcm_data *bcm;
> + struct hci_uart *hu = (struct hci_uart *)arg;
> + struct bcm_h4_struct *h4 = hu->priv;
> + int status;
>
> - BT_DBG("hu %p", hu);
> + BT_DBG("bcm_idle_timeout hu %p", hu);
> +
> + /* Suspend/resume operations are serialized */
> + mutex_lock(&plock);
> +
> + if (!h4->is_suspended) {
> + /* Flow control the port if configured */
> + suspend_notification(hu);
> +
> + /* Suspend the device */
> + status = btbcm_uart_control(BTBCM_UART_ACTION_SUSPEND,
> + h4->device_context, NULL, NULL);
> + if (status)
> + BT_DBG("bcm_idle_timeout failed to suspend device %d",
> + status);
> + }
>
> - bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
> - if (!bcm)
> + mutex_unlock(&plock);
> +}
> +
> +/*
> + * Initialize protocol
> + */
> +static int bcm_h4_open(struct hci_uart *hu)
> +{
> + struct btbcm_uart_callbacks callbacks;
> + unsigned long callbacks_size = sizeof(callbacks);
> + int status;
> + struct bcm_h4_struct *h4;
> + struct tty_struct *tty = hu->tty;
> +
> + BT_DBG("bcm_h4_open hu %p", hu);
> +
> + h4 = kzalloc(sizeof(*h4), GFP_KERNEL);
> + if (!h4)
> return -ENOMEM;
>
> - skb_queue_head_init(&bcm->txq);
> + skb_queue_head_init(&h4->txq);
> + hu->priv = h4;
> + h4->hu = hu;
> + h4->is_suspended = false;
> + h4->parse_recv = false;
> + h4->rsize = 0;
> +
> + /* Configure callbacks on the driver */
> + callbacks.interface_version = BTBCM_UART_INTERFACE_VERSION;
> + callbacks.context = hu;
> + strcpy(callbacks.name, tty->name);
> + callbacks.p_suspend = suspend_notification;
> + callbacks.p_resume = resume_notification;
> + callbacks.p_wakeup = wakeup_notification;
> + status = btbcm_uart_control(BTBCM_UART_ACTION_CONFIGURE_CALLBACKS,
> + NULL, &callbacks, &callbacks_size);
> + if (status) {
> + BT_DBG("bcm_h4_open failed to set driver callbacks %d", status);
> + return status;
> + }
> + if (callbacks_size != sizeof(void *)) {
> + BT_DBG("bcm_h4_open got back %d bytes from callbacks?!",
> + (int)callbacks_size);
> + return -EMSGSIZE;
> + }
> + memcpy(&h4->device_context, &callbacks, sizeof(void *));
> + BT_DBG("bcm_h4_open callbacks context %p", h4->device_context);
> +
> + /* Retrieve device parameters */
> + callbacks_size = sizeof(h4->pars);
> + status = btbcm_uart_control(BTBCM_UART_ACTION_GET_PARAMETERS,
> + h4->device_context, &h4->pars,
> + &callbacks_size);
> + if (status) {
> + BT_DBG("bcm_h4_open failed to get dev parameters %d", status);
> + return status;
> + }
> + BT_DBG("Pars ver %d csleep %d dalow %d balow %d idle %d",
> + h4->pars.interface_version, h4->pars.configure_sleep,
> + h4->pars.dev_wake_active_low, h4->pars.bt_wake_active_low,
> + h4->pars.idle_timeout_in_secs);
> +
> + /* Cycle power to make sure the device is in the known state */
> + status = btbcm_uart_control(BTBCM_UART_ACTION_POWER_OFF,
> + h4->device_context, NULL, NULL);
> + if (status) {
> + BT_DBG("bcm_h4_open failed to power off %d", status);
> + } else {
> + status = btbcm_uart_control(BTBCM_UART_ACTION_POWER_ON,
> + h4->device_context, NULL, NULL);
> + if (status)
> + BT_DBG("bcm_h4_open failed to power on %d", status);
> + }
> +
> + /* Start the idle timer */
> + if (h4->pars.configure_sleep) {
> + setup_timer(&h4->timer, bcm_idle_timeout, (unsigned long)hu);
> + if (h4->pars.configure_sleep)
> + mod_timer(&h4->timer, jiffies + msecs_to_jiffies(
> + h4->pars.idle_timeout_in_secs * 1000));
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Flush protocol data
> + */
> +static int bcm_h4_flush(struct hci_uart *hu)
> +{
> + struct bcm_h4_struct *h4 = hu->priv;
>
> - hu->priv = bcm;
> + BT_DBG("bcm_h4_flush hu %p", hu);
> +
> + skb_queue_purge(&h4->txq);
> return 0;
> }
>
> -static int bcm_close(struct hci_uart *hu)
> +/*
> + * Make sure we're awake
> + * (called when the resumed state is required)
> + */
> +static void bcm_ensure_wakeup(struct hci_uart *hu)
> {
> - struct bcm_data *bcm = hu->priv;
> + struct bcm_h4_struct *h4 = hu->priv;
> + int status;
> +
> + if (!h4->pars.configure_sleep)
> + return;
> +
> + /* Suspend/resume operations are serialized */
> + mutex_lock(&plock);
> +
> + /* Nothing to do if resumed already */
> + if (!h4->is_suspended) {
> + mutex_unlock(&plock);
>
> - BT_DBG("hu %p", hu);
> + /* Just reset the timer */
> + status = mod_timer(&h4->timer, jiffies + msecs_to_jiffies(
> + h4->pars.idle_timeout_in_secs * 1000));
> + return;
> + }

Using a timer is pretty dangerous. We are trying to get away from them as much as possible unless we really need them. Can we just use a delayed work struct here?

> +
> + /* Wakeup the device */
> + status = btbcm_uart_control(BTBCM_UART_ACTION_RESUME,
> + h4->device_context, NULL, NULL);
> + if (status)
> + BT_DBG("bcm_ensure_wakeup failed to resume driver %d", status);
>
> - skb_queue_purge(&bcm->txq);
> - kfree_skb(bcm->rx_skb);
> - kfree(bcm);
> + /* Unflow control the port if configured */
> + resume_notification(hu);
> +
> + mutex_unlock(&plock);
> +}
> +
> +/*
> + * Close protocol
> + */
> +static int bcm_h4_close(struct hci_uart *hu)
> +{
> + struct btbcm_uart_callbacks callbacks;
> + unsigned long callbacks_size = sizeof(callbacks);
> + struct bcm_h4_struct *h4 = hu->priv;
> + int status;
>
> hu->priv = NULL;
> +
> + BT_DBG("bcm_h4_close hu %p", hu);
> +
> + /* If we're being closed, we must suspend */
> + if (h4->pars.configure_sleep) {
> + mutex_lock(&plock);
> +
> + if (!h4->is_suspended) {
> + /* Flow control the port */
> + suspend_notification(hu);
> +
> + /* Suspend the device */
> + status = btbcm_uart_control(BTBCM_UART_ACTION_SUSPEND,
> + h4->device_context, NULL,
> + NULL);
> + if (status) {
> + BT_DBG("bcm_h4_close suspend driver fail %d",
> + status);
> + }
> + }
> +
> + mutex_unlock(&plock);
> +
> + del_timer_sync(&h4->timer);
> + }
> +
> + /* Power off the device if possible */
> + status = btbcm_uart_control(BTBCM_UART_ACTION_POWER_OFF,
> + h4->device_context, NULL, NULL);
> + if (status)
> + BT_DBG("bcm_h4_close failed to power off %d", status);
> +
> + /* de-configure callbacks on the driver */
> + callbacks.interface_version = BTBCM_UART_INTERFACE_VERSION;
> + callbacks.context = hu;
> + callbacks.p_suspend = NULL;
> + callbacks.p_resume = NULL;
> + callbacks.p_wakeup = NULL;
> + status = btbcm_uart_control(BTBCM_UART_ACTION_CONFIGURE_CALLBACKS,
> + h4->device_context, &callbacks,
> + &callbacks_size);
> + if (status)
> + BT_DBG("bcm_h4_close failed to reset drv callbacks %d", status);
> + skb_queue_purge(&h4->txq);
> +
> + hu->priv = NULL;
> + kfree(h4);
> +
> return 0;
> }
>
> -static int bcm_flush(struct hci_uart *hu)
> +/*
> + * Enqueue frame for transmittion (padding, crc, etc)
> + */
> +static int bcm_h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
> {
> - struct bcm_data *bcm = hu->priv;
> + struct bcm_h4_struct *h4 = hu->priv;
> +
> + BT_DBG("bcm_h4_enqueue hu %p skb %p type %d len %d (%x %x %x)",
> + hu, skb, bt_cb(skb)->pkt_type, skb->len,
> + skb->data[0], skb->data[1], skb->data[2]);
>
> - BT_DBG("hu %p", hu);
> + /* Make sure we're resumed */
> + bcm_ensure_wakeup(hu);
>
> - skb_queue_purge(&bcm->txq);
> + /* Prepend skb with frame type */
> + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
> + skb_queue_tail(&h4->txq, skb);
>
> return 0;
> }
>
> -static int bcm_setup(struct hci_uart *hu)
> +/*
> + * HCI event processing if our own parsing of events is required
> + * NOTE: return 0 if the event is not to be passed up to BlueZ
> + */
> +static bool bcm_process_hci_event(struct hci_uart *hu)
> +{
> + struct bcm_h4_struct *h4 = hu->priv;
> + int status;
> + unsigned char evt_code = h4->reassembly[1];
> + unsigned char len = h4->reassembly[2];
> +
> + BT_DBG("bcm_process_hci_event %02x event %02x len %02x %02x",
> + h4->reassembly[0], evt_code, len, h4->reassembly[3]);
> +
> + /* switch (evt_code) { case HCI_EV_CMD_COMPLETE: break } */
> +
> + status = hci_recv_stream_fragment(hu->hdev, h4->reassembly, h4->rsize);
> + if (status < 0)
> + BT_ERR("bcm_process_hci_event - reassembly failed %d", status);
> + return true;
> +}
> +
> +/*
> + * ACL data processing if our own parsing of data is required
> + * NOTE: return 0 if the data is not to be passed up to BlueZ
> + */
> +static bool bcm_process_acl_data(struct hci_uart *hu)
> {
> - BT_DBG("hu %p", hu);
> + struct bcm_h4_struct *h4 = hu->priv;
> + int ret;
>
> - hu->hdev->set_bdaddr = btbcm_set_bdaddr;
> + BT_DBG("bcm_process_acl_data %02x %02x %02x %02x %02x",
> + h4->reassembly[0], h4->reassembly[1], h4->reassembly[2],
> + h4->reassembly[3], h4->reassembly[4]);
>
> - return btbcm_setup_patchram(hu->hdev);
> + ret = hci_recv_stream_fragment(hu->hdev, h4->reassembly, h4->rsize);
> + if (ret < 0)
> + BT_ERR("bcm_process_acl_data - Frame Reassembly Failed");
> + return true;
> }
>
> -static const struct h4_recv_pkt bcm_recv_pkts[] = {
> - { H4_RECV_ACL, .recv = hci_recv_frame },
> - { H4_RECV_SCO, .recv = hci_recv_frame },
> - { H4_RECV_EVENT, .recv = hci_recv_frame },
> -};
> +/*
> + * Fragment parsing in the active filtering phase
> + * (not currently actively used)
> + */
> +static void parse_fragment(struct hci_uart *hu, unsigned char *data, int count)
> +{
> + struct bcm_h4_struct *h4 = hu->priv;
> +
> + if (h4->rsize)
> + BT_DBG("parse_fragment type %x expected %d",
> + h4->reassembly[0], h4->rexpected);
> +
> + while (count) {
> + if (!h4->rsize) {
> + /* Start of the frame */
> + h4->reassembly[0] = *data++;
> + h4->rsize++;
> + count--;
> + continue;
> + }
> +
> + switch (h4->reassembly[0]) {
> + case HCI_EVENT_PKT:
> + if (h4->rsize == 1) {
> + /* event proper */
> + h4->reassembly[h4->rsize++] = *data++;
> + count--;
> + continue;
> + }
> + if (h4->rsize == 2) {
> + /* length */
> + h4->rexpected = *data;
> + h4->reassembly[h4->rsize++] = *data++;
> + count--;
> + BT_DBG("evthdr len %d, left %d", h4->rexpected, count);
> + continue;
> + }
> + if (count >= h4->rexpected) {
> + memcpy(&h4->reassembly[h4->rsize], data, h4->rexpected);
> + h4->rsize += h4->rexpected;
> + data += h4->rexpected;
> + count -= h4->rexpected;
> + bcm_process_hci_event(hu);
> + h4->rsize = 0; /* starting anew */
> + continue;
> + }
> + /* only piece of the event received */
> + memcpy(&h4->reassembly[h4->rsize], data, count);
> + h4->rsize += count;
> + data += count;
> + h4->rexpected -= count;
> + count = 0;
> + break;
> +
> + case HCI_ACLDATA_PKT:
> + if ((h4->rsize == 1) || (h4->rsize == 2) || (h4->rsize == 3)) {
> + /* handle and first byte of length */
> + h4->reassembly[h4->rsize++] = *data++;
> + count--;
> + continue;
> + }
> + if (h4->rsize == 4) {
> + /* last byte of the length */
> + h4->reassembly[h4->rsize++] = *data++;
> + h4->rexpected = h4->reassembly[h4->rsize - 2] +
> + (h4->reassembly[h4->rsize - 1] << 8);
> + count--;
> + BT_DBG("dathdr len %d, left %d", h4->rexpected, count);
> + continue;
> + }
> + if (count >= h4->rexpected) {
> + memcpy(&h4->reassembly[h4->rsize], data, h4->rexpected);
> + h4->rsize += h4->rexpected;
> + data += h4->rexpected;
> + count -= h4->rexpected;
> + bcm_process_acl_data(hu);
> + h4->rsize = 0; /* starting anew */
> + continue;
> + }
> + /* only piece of data received */
> + memcpy(&h4->reassembly[h4->rsize], data, count);
> + h4->rsize += count;
> + data += count;
> + h4->rexpected -= count;
> + count = 0;
> + break;
> +
> + default: /* Note that SCO may NOT come through the UART */

You can not really make this statement. This has to work even with platform where SCO might actually come through an UART. The automotive platforms historically preferred SCO over UART.

Then again, I am not even reviewing this piece of code. Use the h4_recv_buf helper. I send around examples for using it even with vendor packets like what Nokia used in their platforms.

> + if (count >= 3)
> + BT_DBG("unexpected pkt type of %x (%02x %02x %02x)?!",
> + h4->reassembly[0], data[0], data[1], data[2]);
> + else if (count == 2)
> + BT_DBG("unexpected pkt type of %x (%02x %02x)?!",
> + h4->reassembly[0], data[0], data[1]);
> + else if (count == 1)
> + BT_DBG("unexpected pkt type of %x (%02x)?!",
> + h4->reassembly[0], data[0]);
> + else
> + BT_DBG("unexpected pkt type of %x?!",
> + h4->reassembly[0]);
> + h4->rsize = 0;
> + return;
> + }
> + }
> +}
>
> -static int bcm_recv(struct hci_uart *hu, const void *data, int count)
> +/*
> + * Data indication from the line discipline
> + */
> +static int bcm_h4_recv(struct hci_uart *hu, void *data, int count)
> {
> - struct bcm_data *bcm = hu->priv;
> + struct bcm_h4_struct *h4 = hu->priv;
> + int ret;
>
> - if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
> + BT_DBG("bcm_h4_recv hu %p len %d", hu, count);
> +
> + if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) {
> + BT_DBG("h4_recv UART not registered!");
> return -EUNATCH;
> + }

All these debug additions are cluttering the patch review. Makes it a lot hardware for me to review. It is fine if you keep them in a series as temporary patches, but here it is just causing extra work for me.

>
> - bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
> - bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
> - if (IS_ERR(bcm->rx_skb)) {
> - int err = PTR_ERR(bcm->rx_skb);
> - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
> - return err;
> + /* Make sure we're resumed */
> + bcm_ensure_wakeup(hu);
> +
> + /* If we're in the active phase, parse what we get */
> + if (h4->parse_recv) {
> + parse_fragment(hu, data, count);
> + } else {
> + ret = hci_recv_stream_fragment(hu->hdev, data, count);

The hci_recv_stream_fragement function is gone from upstream. You can not use it anymore.

> + if (ret < 0) {
> + BT_ERR("bcm_h4_recv: frame reassembly failed");
> + return ret;
> + }
> }
>
> return count;
> }
>
> -static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
> +/*
> + * Line discipline is grabbing a packet from the tx queue
> + */
> +static struct sk_buff *bcm_h4_dequeue(struct hci_uart *hu)
> {

This renaming is really not helping to make the patch look simpler.

> - struct bcm_data *bcm = hu->priv;
> + struct bcm_h4_struct *h4 = hu->priv;
> + int is_qempty = skb_queue_empty(&h4->txq);
>
> - BT_DBG("hu %p skb %p", hu, skb);
> + if (!is_qempty)
> + BT_DBG("bcm_h4_dequeue with non-empty queue");
> + return skb_dequeue(&h4->txq);
> +}
>
> - /* Prepend skb with frame type */
> - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
> - skb_queue_tail(&bcm->txq, skb);
> +/*
> + * Callbacks from the BCMBT_UART device
> + */
>
> - return 0;
> +/*
> + * The platform is suspending. Stop UART activity
> + */
> +static void suspend_notification(void *context)
> +{
> + struct ktermios ktermios;
> + struct hci_uart *hu = (struct hci_uart *)context;
> + struct bcm_h4_struct *h4 = hu->priv;
> + struct tty_struct *tty = hu->tty;
> + int status;
> + unsigned int set = 0;
> + unsigned int clear = 0;
> +
> + BT_DBG("suspend_notification with is_suspended %d", h4->is_suspended);
> +
> + if (!h4->pars.configure_sleep)
> + return;
> +
> + if (!h4->is_suspended) {
> + if (h4->pars.manual_fc) {
> + /* Disable hardware flow control */
> + ktermios = tty->termios;
> + ktermios.c_cflag &= ~CRTSCTS;
> + status = tty_set_termios(tty, &ktermios);
> + if (status)
> + BT_DBG("suspend_notification dis fc fail %d",
> + status);
> + else
> + BT_DBG("suspend_notification hw fc disabled");
> +
> + /* Clear RTS to prevent the device from sending */
> + /* (most PCs need OUT2 to enable interrupts) */
> + status = tty->driver->ops->tiocmget(tty);
> + BT_DBG("suspend_notification cur tiocm 0x%x", status);
> + set &= ~(TIOCM_OUT2 | TIOCM_RTS);
> + clear = ~set;
> + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
> + TIOCM_OUT2 | TIOCM_LOOP;
> + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
> + TIOCM_OUT2 | TIOCM_LOOP;
> + status = tty->driver->ops->tiocmset(tty, set, clear);
> + if (status)
> + BT_DBG("suspend_notification clr RTS fail %d",
> + status);
> + else
> + BT_DBG("suspend_notification RTS cleared");
> + status = tty->driver->ops->tiocmget(tty);
> + BT_DBG("suspend_notification end tiocm 0x%x", status);
> + }

I think that hci_ldisc.c should maybe provide common helpers for clearing RTS etc. I would work on making that generic and splitting it out so it can be used by others. Seems like the Atheros UART driver needs that as well. However it looks a lot simpler there. Anyway, it should be general and not duplicated in every sub driver.

> +
> + /* Once this callback returns, driver suspends BT via GPIO */
> + h4->is_suspended = true;
> + }
> +}
> +
> +/*
> + * The platform is resuming. Resume UART activity.
> + */
> +static void resume_notification(void *context)
> +{
> + struct ktermios ktermios;
> + struct hci_uart *hu = (struct hci_uart *)context;
> + struct bcm_h4_struct *h4 = hu->priv;
> + struct tty_struct *tty = hu->tty;
> + int status;
> + unsigned int set = 0;
> + unsigned int clear = 0;
> +
> + BT_DBG("resume_notification with is_suspended %d", h4->is_suspended);
> +
> + if (!h4->pars.configure_sleep)
> + return;
> +
> + /* When this callback executes, the device has woken up already */
> + if (h4->is_suspended) {
> + h4->is_suspended = false;
> +
> + if (h4->pars.manual_fc) {
> + status = tty->driver->ops->tiocmget(tty);
> + BT_DBG("resume_notification cur tiocm 0x%x", status);
> + set |= (TIOCM_OUT2 | TIOCM_RTS);
> + clear = ~set;
> + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
> + TIOCM_OUT2 | TIOCM_LOOP;
> + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
> + TIOCM_OUT2 | TIOCM_LOOP;
> + status = tty->driver->ops->tiocmset(tty, set, clear);
> + if (status)
> + BT_DBG("resume_notification set RTS fail %d",
> + status);
> + else
> + BT_DBG("resume_notification RTS set");
> +
> + /* Re-enable hardware flow control */
> + ktermios = tty->termios;
> + ktermios.c_cflag |= CRTSCTS;
> + status = tty_set_termios(tty, &ktermios);
> + if (status)
> + BT_DBG("resume_notification enable fc fail %d",
> + status);
> + else
> + BT_DBG("resume_notification hw fc re-enabled");
> + }
> + }
> +
> + /* If we're resumed, the idle timer must be running */
> + status = mod_timer(&h4->timer, jiffies +
> + msecs_to_jiffies(h4->pars.idle_timeout_in_secs * 1000));
> }
>
> -static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
> +/*
> + * The BT device is resuming. Resume UART activity if suspended
> + */
> +static void wakeup_notification(void *context)
> {
> - struct bcm_data *bcm = hu->priv;
> + struct ktermios ktermios;
> + struct hci_uart *hu = (struct hci_uart *)context;
> + struct bcm_h4_struct *h4 = hu->priv;
> + struct tty_struct *tty = hu->tty;
> + int status;
> + unsigned int set = 0;
> + unsigned int clear = 0;
> +
> + if (!h4->pars.configure_sleep)
> + return;
> +
> + status = tty->driver->ops->tiocmget(tty);
> + BT_DBG("wakeup_notification hu %p current tiocm 0x%x", hu, status);
> + if (h4->is_suspended) {
> + if (h4->pars.manual_fc) {
> + set |= (TIOCM_OUT2 | TIOCM_RTS);
> + clear = ~set;
> + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
> + TIOCM_OUT2 | TIOCM_LOOP;
> + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
> + TIOCM_OUT2 | TIOCM_LOOP;
> + status = tty->driver->ops->tiocmset(tty, set, clear);
> + if (status)
> + BT_DBG("wakeup_notification set RTS fail %d",
> + status);
> + else
> + BT_DBG("wakeup_notification RTS set");
> +
> + /* Re-enable hardware flow control */
> + ktermios = tty->termios;
> + ktermios.c_cflag |= CRTSCTS;
> + status = tty_set_termios(tty, &ktermios);
> + if (status)
> + BT_DBG("wakeup_notification fc-en failure %d",
> + status);
> + else
> + BT_DBG("wakeup_notification hw fc re-enabled");
> + }
> +
> + h4->is_suspended = false;
> + }
>
> - return skb_dequeue(&bcm->txq);
> + /* If we're resumed, the idle timer must be running */
> + status = mod_timer(&h4->timer, jiffies + msecs_to_jiffies(
> + h4->pars.idle_timeout_in_secs * 1000));
> }
>
> -static const struct hci_uart_proto bcm_proto = {
> +/*
> + * Device setup that follows the protocol open
> + */
> +static int bcm_h4_setup(struct hci_uart *hu)
> +{
> + struct bcm_h4_struct *h4 = hu->priv;
> + int status;
> + struct sk_buff *skb;
> + unsigned char sleep_pars[] = {
> + 0x01, /* sleep mode 1=UART */
> + 0x02, /* idle threshold HOST (value * 300ms) */
> + 0x02, /* idle threshold HC (value * 300ms) */
> + 0x01, /* BT_WAKE active mode - 1=active high, 0 = active low */
> + 0x00, /* HOST_WAKE active mode - 1=active high, 0 = active low */
> + 0x01, /* Allow host sleep during SCO - FALSE */
> + 0x01, /* combine sleep mode and LPM - 1 == TRUE */
> + 0x00, /* enable tristate control of UART TX line - FALSE */
> + 0x00, /* USB auto-sleep on USB SUSPEND */
> + 0x00, /* USB USB RESUME timeout (seconds) */
> + 0x00, /* Pulsed Host Wake */
> + 0x00 /* Enable Break To Host */
> + };
> + unsigned char pcm_int_pars[] = {
> + 0x00, /* 0=PCM routing, 1=SCO over HCI */
> + 0x02, /* 0=128Kbps,1=256Kbps,2=512Kbps,3=1024Kbps,4=2048Kbps */
> + 0x00, /* short frame sync 0=short, 1=long */
> + 0x00, /* sync mode 0=slave, 1=master */
> + 0x00 /* clock mode 0=slave, 1=master */
> + };
> + unsigned char pcm_format_pars[] = {
> + 0x00, /* LSB_First 1=TRUE, 0=FALSE */
> + 0x00, /* Fill_Value (use 0-7 for fill bits value) */
> + 0x02, /* Fill_Method (2=sign extended) */
> + 0x03, /* Fill_Num # of fill bits 0-3) */
> + 0x01 /* Right_Justify 1=TRUE, 0=FALSE */
> + };

This is violating the indentation coding style.

> + unsigned char time_slot_number = 0;
> +
> + BT_DBG("bcm_h4_setup hu %p", hu);
> +
> + /* Bring the UART into known default state */
> + status = btbcm_init_uart(hu);
> + if (status) {
> + BT_DBG("bcm_h4_setup failed to init BT device %d", status);
> + return status;
> + }
> +
> + /* Basic sanity check */
> + skb = __hci_cmd_sync(hu->hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb)) {
> + status = PTR_ERR(skb);
> + BT_ERR("bcm_h4_setup HCI Reset failed (%d)", status);
> + return status;
> + }
> + kfree_skb(skb);
> + BT_DBG("bcm_h4_setup HCI Reset succeeded");
> +
> + /* Set the new baud rate */
> + status = btbcm_set_baud_rate(hu,
> + h4->pars.baud_rate_before_config_download);
> + if (status) {
> + BT_ERR("bcm_h4_setup set_baud_rate faiilure %d", status);
> + return status;
> + }
> +
> + hu->hdev->set_bdaddr = btbcm_set_bdaddr;
> +
> + /* Download the firmware and reconfigure the UART afterwards */
> + status = btbcm_setup_patchram(hu->hdev);
> + if (status) {
> + BT_ERR("bcm_h4_setup setup_patchram faiilure %d", status);
> + return status;
> + }
> +
> + /* Configure SCO PCM parameters */
> + if (h4->pars.configure_audio) {
> + pcm_int_pars[0] = h4->pars.PCMRouting;
> + pcm_int_pars[1] = h4->pars.PCMInCallBitclock;
> + pcm_int_pars[2] = h4->pars.PCMShortFrameSync;
> + pcm_int_pars[3] = h4->pars.PCMSyncMode;
> + pcm_int_pars[4] = h4->pars.PCMClockMode;

I really get the feeling we want a proper struct for this HCI command. I also wonder if this should be actual some generic function exposed by btbcm.ko. It is really not limited to UART device. It is also possible to build an USB based platform that has SCO routed over PCM.

> + skb = __hci_cmd_sync(hu->hdev, 0xfc1c, sizeof(pcm_int_pars),
> + pcm_int_pars, HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb)) {
> + status = PTR_ERR(skb);
> + BT_ERR("bcm_h4_setup PCM INT VSC failed (%d)", status);
> + return status;
> + }
> + kfree_skb(skb);
> + BT_DBG("bcm_h4_setup PCM INT Parameters VSC succeeded");
> +
> + pcm_format_pars[0] = h4->pars.PCMLSBFirst;
> + pcm_format_pars[1] = h4->pars.PCMFillValue;
> + pcm_format_pars[2] = h4->pars.PCMFillMethod;
> + pcm_format_pars[3] = h4->pars.PCMFillNum;
> + pcm_format_pars[4] = h4->pars.PCMRightJustify;
> + skb = __hci_cmd_sync(hu->hdev, 0xfc1e, sizeof(pcm_format_pars),
> + pcm_format_pars, HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb)) {
> + status = PTR_ERR(skb);
> + BT_ERR("bcm_h4_setup PCM Format VSC failed (%d)",
> + status);
> + return status;
> + }
> + kfree_skb(skb);
> + BT_DBG("bcm_h4_setup PCM Format VSC succeeded");
> +
> + skb = __hci_cmd_sync(hu->hdev, 0xfc22, sizeof(time_slot_number),
> + &time_slot_number, HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb)) {
> + status = PTR_ERR(skb);
> + BT_ERR("bcm_h4_setup SCO Time Slot VSC failed (%d)",
> + status);
> + return status;
> + }
> + kfree_skb(skb);
> + BT_DBG("bcm_h4_setup SCO Time Slot VSC succeeded");
> + }
> +
> + /* Configure device's suspend/resume operation */
> + if (h4->pars.configure_sleep) {
> + /* Override the default */
> + sleep_pars[3] = (unsigned char)!h4->pars.bt_wake_active_low;
> + sleep_pars[4] = (unsigned char)!h4->pars.dev_wake_active_low;
> + skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_pars),
> + sleep_pars, HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb)) {
> + status = PTR_ERR(skb);
> + BT_ERR("bcm_h4_setup Sleep VSC failed (%d)", status);
> + return status;
> + }
> + kfree_skb(skb);
> + BT_DBG("bcm_h4_setup Set Sleep Parameters VSC succeeded");
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Protocol callbacks
> + */
> +static struct hci_uart_proto h4p = {

Keep this as bcm_proto.

> .id = HCI_UART_BCM,
> - .name = "BCM",
> - .open = bcm_open,
> - .close = bcm_close,
> - .flush = bcm_flush,
> - .setup = bcm_setup,
> - .recv = bcm_recv,
> - .enqueue = bcm_enqueue,
> - .dequeue = bcm_dequeue,
> + .name = "UARTBCM”,

Please stop renaming things. This is a UART driver and thus no need to repeat that.

> + .open = bcm_h4_open,
> + .close = bcm_h4_close,
> + .flush = bcm_h4_flush,
> + .setup = bcm_h4_setup,
> + .recv = bcm_h4_recv,
> + .enqueue = bcm_h4_enqueue,
> + .dequeue = bcm_h4_dequeue,
> };
>
> +/*
> + * Protocol init
> + */
> int __init bcm_init(void)
> {
> - return hci_uart_register_proto(&bcm_proto);
> + int status = hci_uart_register_proto(&h4p);
> +
> + if (!status)
> + BT_INFO("Broadcom H4 protocol initialized");
> + else
> + BT_ERR("Broadcom H4 protocol registration failed %d", status);
> +
> + return status;

I move this error printing into generic code and so please do not modify this and revert what I did.
> }
>
> +/*
> + * Protocol shutdown
> + */
> int __exit bcm_deinit(void)
> {
> - return hci_uart_unregister_proto(&bcm_proto);
> + BT_INFO("Broadcom H4 protocol de-initialized");
> + return hci_uart_unregister_proto(&h4p);
> }
> +

No extra empty line at the end of a file.

Regards

Marcel


2015-05-07 20:25:29

by Ilya Faenson

[permalink] [raw]
Subject: [RFC 2/2] Broadcom Bluetooth UART device driver

This code implements the Broadcom Bluetooth device driver.
It manages device configuration, firmware download and
power management.

Signed-off-by: Ilya Faenson <[email protected]>
---
drivers/bluetooth/Kconfig | 35 +-
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/btbcm.c | 173 ++++++++-
drivers/bluetooth/btbcm.h | 19 +-
drivers/bluetooth/btbcm_uart.c | 679 +++++++++++++++++++++++++++++++++
drivers/bluetooth/btbcm_uart.h | 90 +++++
drivers/bluetooth/hci_bcm.c | 838 +++++++++++++++++++++++++++++++++++++----
7 files changed, 1745 insertions(+), 90 deletions(-)
create mode 100644 drivers/bluetooth/btbcm_uart.c
create mode 100644 drivers/bluetooth/btbcm_uart.h

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index ed5c273..1bfe36b 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -8,6 +8,12 @@ config BT_INTEL
config BT_BCM
tristate
select FW_LOADER
+ help
+ This feature is required if you want to use Broadcom Bluetooth
+ over both USB and UART.
+
+ Say Y here to compile support for Broadcom Bluetooth
+ kernel or say M to compile it as module (btusb).

config BT_HCIBTUSB
tristate "HCI USB driver"
@@ -22,15 +28,16 @@ config BT_HCIBTUSB
kernel or say M to compile it as module (btusb).

config BT_HCIBTUSB_BCM
- bool "Broadcom protocol support"
+ bool "Broadcom USB support"
depends on BT_HCIBTUSB
select BT_BCM
default y
help
- The Broadcom protocol support enables firmware and patchram
- download support for Broadcom Bluetooth controllers.
+ Broadcom Bluetooth USB driver support.
+ The Broadcom USB support enables firmware and patchram
+ download for Broadcom Bluetooth USB controllers.

- Say Y here to compile support for Broadcom protocol.
+ Say Y here to compile support for Broadcom USB.

config BT_HCIBTSDIO
tristate "HCI SDIO driver"
@@ -125,15 +132,25 @@ config BT_HCIUART_INTEL
Say Y here to compile support for Intel protocol.

config BT_HCIUART_BCM
- bool "Broadcom protocol support"
- depends on BT_HCIUART
+ bool "Broadcom BT UART serial support"
select BT_HCIUART_H4
+ select BT_UART_BCM
select BT_BCM
help
- The Broadcom protocol support enables Bluetooth HCI over serial
- port interface for Broadcom Bluetooth controllers.
+ HCI_UART_BCM is a protocol for initializing, managing and
+ communicating with Broadcom UART Bluetooth devices.
+ This protocol initializes chips and power-manages them.
+ Enable this if you have serial Broadcom Bluetooth device.
+
+ Say Y here to compile support for Broadcom UART protocol.
+
+config BT_UART_BCM
+ tristate "Broadcom BT UART driver"
+ depends on BT_HCIUART_H4 && TTY
+ help
+ This driver supports the HCI_UART_BT protocol.

- Say Y here to compile support for Broadcom protocol.
+ It manages Bluetooth UART device properties and GPIOs.

config BT_HCIBCM203X
tristate "HCI BCM203x USB driver"
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index dd0d9c4..0e5fd66 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_WILINK) += btwilink.o
obj-$(CONFIG_BT_BCM) += btbcm.o
+obj-$(CONFIG_BT_UART_BCM) += btbcm_uart.o

btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 4bba866..3e9ac30 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -2,7 +2,8 @@
*
* Bluetooth support for Broadcom devices
*
- * Copyright (C) 2015 Intel Corporation
+ * Copyright (C) 2015 Intel Corporation
+ * Copyright (C) 2015 Broadcom Corporation
*
*
* This program is free software; you can redistribute it and/or modify
@@ -15,24 +16,22 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/

#include <linux/module.h>
#include <linux/firmware.h>
+#include <linux/tty.h>
#include <asm/unaligned.h>

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>

+#include "hci_uart.h"
#include "btbcm.h"

-#define VERSION "0.1"
+#define VERSION "0.2"

-#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
+#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00} })

int btbcm_check_bdaddr(struct hci_dev *hdev)
{
@@ -43,6 +42,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
+
BT_ERR("%s: BCM: Reading device address failed (%d)",
hdev->name, err);
return err;
@@ -159,6 +159,8 @@ int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
}

/* 250 msec delay after Launch Ram completes */
+ BT_INFO("%s: BCM: Delaying upon the patch download completion...",
+ hdev->name);
msleep(250);

done:
@@ -174,6 +176,7 @@ static int btbcm_reset(struct hci_dev *hdev)
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
+
BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err);
return err;
}
@@ -246,8 +249,10 @@ static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
static const struct {
u16 subver;
const char *name;
+ u32 baud_rate; /* operational baud rate */
} bcm_uart_subver_table[] = {
- { 0x410e, "BCM43341B0" }, /* 002.001.014 */
+ { 0x410e, "BCM43341B0", 3000000}, /* 002.001.014 */
+ { 0x610c, "BCM4354_003.001.012.0306.0659", 3000000}, /* 003.001.012 */
{ }
};

@@ -268,6 +273,133 @@ static const struct {
{ }
};

+/*
+ * Set the UART into the defaults
+ */
+int btbcm_init_uart(struct hci_uart *hu)
+{
+ struct ktermios ktermios;
+ struct tty_struct *tty = hu->tty;
+ int status, speed;
+
+ /* Bring UART into default setting at 115200 */
+ if (tty->ldisc->ops->flush_buffer)
+ tty->ldisc->ops->flush_buffer(tty);
+ tty_driver_flush_buffer(tty);
+ ktermios = tty->termios;
+ BT_DBG("init_uart def flags c_o %x c_l %x c_c %x spd %d/%d",
+ ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
+ ktermios.c_ispeed, ktermios.c_ospeed);
+ ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+ | INLCR | IGNCR | ICRNL | IXON);
+ ktermios.c_oflag &= ~OPOST;
+ ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ ktermios.c_cflag &= ~(CSIZE | PARENB | CBAUD);
+ ktermios.c_cflag |= CS8;
+ ktermios.c_cflag |= CRTSCTS;
+ ktermios.c_cflag |= B115200;
+ ktermios.c_ispeed = 115200;
+ ktermios.c_ospeed = 115200;
+ status = tty_set_termios(tty, &ktermios);
+ if (status) {
+ BT_DBG("init_uart set_termios failure %d", status);
+ return status;
+ }
+
+ speed = tty_get_baud_rate(tty);
+ BT_DBG("init_uart set_termios completed, spd %d", speed);
+ ktermios = tty->termios;
+ BT_DBG("init_uart new flags c_o %x c_l %x c_c %x spd %d/%d",
+ ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
+ ktermios.c_ispeed, ktermios.c_ospeed);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_init_uart);
+
+/*
+ * Set the baud rate on the UART and the device
+ */
+int btbcm_set_baud_rate(struct hci_uart *hu, int baud_rate)
+{
+ struct ktermios ktermios;
+ struct tty_struct *tty = hu->tty;
+ int status, speed, cflag;
+ struct sk_buff *skb;
+ unsigned char enable = 1;
+ unsigned char baud_rate_vsc_pars[] = {0, 0, 0, 0x10, 0x0e, 0};
+
+ /* If the baud rate is higher than 3000000, change the clock */
+ if (baud_rate > 3000000) {
+ skb = __hci_cmd_sync(hu->hdev, 0xfc45, 1, &enable,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ status = PTR_ERR(skb);
+ return status;
+ }
+
+ kfree_skb(skb);
+ BT_DBG("set_baud_rate write UART 48 MHz VSC succeeded");
+ }
+
+ /* Now let the device know about the rate change */
+ baud_rate_vsc_pars[2] = (unsigned char)(baud_rate & 0xff);
+ baud_rate_vsc_pars[3] = (unsigned char)((baud_rate >> 8) & 0xff);
+ baud_rate_vsc_pars[4] = (unsigned char)((baud_rate >> 16) & 0xff);
+ baud_rate_vsc_pars[5] = (unsigned char)((baud_rate >> 24) & 0xff);
+ skb = __hci_cmd_sync(hu->hdev, 0xfc18, sizeof(baud_rate_vsc_pars),
+ baud_rate_vsc_pars, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ status = PTR_ERR(skb);
+ BT_ERR("set_baud_rate VSC failed (%d)", status);
+ return status;
+ }
+
+ kfree_skb(skb);
+ BT_DBG("set_baud_rate VSC succeeded");
+
+ /* Set UART into this rate as well */
+ ktermios = tty->termios;
+ BT_DBG("set_baud_rate start flags c_o %x c_l %x c_c %x spd %d/%d",
+ ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
+ ktermios.c_ispeed, ktermios.c_ospeed);
+ switch (baud_rate) {
+ case 115200:
+ cflag |= B115200; break;
+ case 921600:
+ cflag |= B921600; break;
+ case 3000000:
+ cflag |= B3000000; break;
+ case 3500000:
+ cflag |= B3500000; break;
+ case 4000000:
+ cflag |= B4000000; break;
+ default:
+ BT_DBG("set_baud_rate unknown rate %d", baud_rate);
+ return -EINVAL;
+ }
+
+ ktermios.c_cflag &= ~CBAUD;
+ ktermios.c_cflag |= cflag;
+ ktermios.c_ispeed = baud_rate;
+ ktermios.c_ospeed = baud_rate;
+ status = tty_set_termios(tty, &ktermios);
+ if (status) {
+ BT_DBG("set_baud_rate set_termios failure %d", status);
+ return status;
+ }
+
+ speed = tty_get_baud_rate(tty);
+ BT_DBG("set_baud_rate set_termios completed, spd %d", speed);
+ ktermios = tty->termios;
+ BT_DBG("set_baud_rate flags c_o %x c_l %x c_c %x spd %d/%d",
+ ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
+ ktermios.c_ispeed, ktermios.c_ospeed);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_set_baud_rate);
+
int btbcm_setup_patchram(struct hci_dev *hdev)
{
char fw_name[64];
@@ -275,7 +407,8 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
const char *hw_name = NULL;
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
- int i, err;
+ int i, err, is_uart = false;
+ struct hci_uart *hu = hci_get_drvdata(hdev);

/* Reset */
err = btbcm_reset(hdev);
@@ -297,14 +430,18 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
if (IS_ERR(skb))
return PTR_ERR(skb);

- BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
+ BT_INFO("%s: BCM: chip id %u, rev 0x%x subver 0x%x",
+ hdev->name, skb->data[1], rev, subver);
kfree_skb(skb);

switch ((rev & 0xf000) >> 12) {
case 0:
+ case 1:
for (i = 0; bcm_uart_subver_table[i].name; i++) {
if (subver == bcm_uart_subver_table[i].subver) {
hw_name = bcm_uart_subver_table[i].name;
+ BT_INFO("UART firmware found: %s", hw_name);
+ is_uart = true;
break;
}
}
@@ -312,7 +449,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
hw_name ? : "BCM");
break;
- case 1:
+
case 2:
/* Read USB Product Info */
skb = btbcm_read_usb_product(hdev);
@@ -345,11 +482,25 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
if (err == -ENOENT)
return 0;

+ /* Once the patch is downloaded, the device is back at default rate */
+ if (is_uart) {
+ err = btbcm_init_uart(hu);
+ if (err)
+ return 0;
+ }
+
/* Reset */
err = btbcm_reset(hdev);
if (err)
return err;

+ if (is_uart) {
+ err = btbcm_set_baud_rate(hu,
+ bcm_uart_subver_table[i].baud_rate);
+ if (err)
+ return 0;
+ }
+
/* Read Local Version Info */
skb = btbcm_read_local_version(hdev);
if (IS_ERR(skb))
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
index eb6ab5f..f7d30c6 100644
--- a/drivers/bluetooth/btbcm.h
+++ b/drivers/bluetooth/btbcm.h
@@ -2,7 +2,8 @@
*
* Bluetooth support for Broadcom devices
*
- * Copyright (C) 2015 Intel Corporation
+ * Copyright (C) 2015 Intel Corporation
+ * Copyright (C) 2015 Broadcom Corporation
*
*
* This program is free software; you can redistribute it and/or modify
@@ -15,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/

#if IS_ENABLED(CONFIG_BT_BCM)
@@ -30,6 +27,8 @@ int btbcm_patchram(struct hci_dev *hdev, const char *firmware);
int btbcm_setup_patchram(struct hci_dev *hdev);
int btbcm_setup_apple(struct hci_dev *hdev);

+int btbcm_init_uart(struct hci_uart *hu);
+int btbcm_set_baud_rate(struct hci_uart *hu, int baud_rate);
#else

static inline int btbcm_check_bdaddr(struct hci_dev *hdev)
@@ -57,4 +56,14 @@ static inline int btbcm_setup_apple(struct hci_dev *hdev)
return 0;
}

+static int btbcm_init_uart(void *hu)
+{
+ return 0;
+}
+
+static int btbcm_set_baud_rate(void *hu, int baud_rate);
+{
+ return 0;
+}
+
#endif
diff --git a/drivers/bluetooth/btbcm_uart.c b/drivers/bluetooth/btbcm_uart.c
new file mode 100644
index 0000000..3308bdb
--- /dev/null
+++ b/drivers/bluetooth/btbcm_uart.c
@@ -0,0 +1,679 @@
+/*
+ *
+ * Bluetooth BCM UART Driver
+ *
+ * Copyright (c) 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+#include <linux/list.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+
+#include "btbcm_uart.h"
+
+static int idleTimeout = 5;
+module_param(idleTimeout, int, 0);
+MODULE_PARM_DESC(idleTimeout, "Bluetooth idle timeout in seconds");
+
+/* Device context */
+struct bcm_device {
+ struct list_head list;
+
+ struct platform_device *pdev;
+ struct gpio_desc *bt_wake_gpio;
+ struct gpio_desc *dev_wake_gpio;
+ struct gpio_desc *reg_on_gpio;
+ int bt_wake_irq;
+ int dev_wake_active_low;
+ int reg_on_active_low;
+ int bt_wake_active_low;
+ u32 configure_sleep;
+ u32 manual_fc;
+ u32 baud_rate_before_config_download;
+ u32 configure_audio;
+ u32 PCMClockMode;
+ u32 PCMFillMethod;
+ u32 PCMFillNum;
+ u32 PCMFillValue;
+ u32 PCMInCallBitclock;
+ u32 PCMLSBFirst;
+ u32 PCMRightJustify;
+ u32 PCMRouting;
+ u32 PCMShortFrameSync;
+ u32 PCMSyncMode;
+
+ char tty_name[64];
+
+ struct btbcm_uart_callbacks protocol_callbacks;
+ struct work_struct wakeup_work;
+};
+
+/* List of BCM BT UART devices */
+static DEFINE_SPINLOCK(device_list_lock);
+static LIST_HEAD(device_list);
+
+/*
+ * Calling the BCM protocol at lower execution priority
+ */
+static void bcm_bt_wakeup_task(struct work_struct *ws)
+{
+ int resume_flag;
+ struct bcm_device *p_bcm_device =
+ container_of(ws, struct bcm_device, wakeup_work);
+
+ if (!p_bcm_device) {
+ BT_DBG("bcm_bt_wakeup_task - failing, no device");
+ return;
+ }
+
+ /* Make sure the device is resumed */
+ resume_flag = !p_bcm_device->dev_wake_active_low;
+ if (p_bcm_device->dev_wake_gpio) {
+ gpiod_set_value(p_bcm_device->dev_wake_gpio, resume_flag);
+ BT_DBG("bcm_bt_wakeup_task - resume %d written, delaying 15 ms",
+ resume_flag);
+ mdelay(15);
+ }
+
+ /* Let the protocol know it's time to wake up */
+ if (p_bcm_device->protocol_callbacks.p_wakeup)
+ p_bcm_device->protocol_callbacks.p_wakeup(
+ p_bcm_device->protocol_callbacks.context);
+}
+
+/*
+ * Interrupt routine for the wake from the device
+ */
+static irqreturn_t bcm_bt_uart_isr(int irq, void *context)
+{
+ unsigned int bt_wake;
+ struct bcm_device *p = (struct bcm_device *)context;
+
+ bt_wake = gpiod_get_value(p->bt_wake_gpio);
+ BT_DBG("bcm_bt_uart_isr with bt_wake of %d (active_low %d), req bh",
+ bt_wake, p->bt_wake_active_low);
+
+ /* Defer the actual processing to the platform work queue */
+ schedule_work(&p->wakeup_work);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Device instance startup
+ */
+static int bcm_bt_uart_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device_node *np = pdev->dev.of_node;
+ const char *tty_name;
+ struct bcm_device *p_bcm_device = NULL;
+
+ p_bcm_device = devm_kzalloc(&pdev->dev, sizeof(*p_bcm_device),
+ GFP_KERNEL);
+ if (!p_bcm_device) {
+ BT_DBG("bcm_bt_uart_probe - failing due to no memory");
+ return -ENOMEM;
+ }
+ p_bcm_device->pdev = pdev;
+ BT_DBG("bcm_bt_uart_probe %p context", p_bcm_device);
+
+ /* Get dev wake GPIO */
+ p_bcm_device->dev_wake_gpio = gpiod_get(&pdev->dev, "bt-wake");
+ BT_DBG("bcm_bt_uart_probe - gpiod_get for bt-wake returned %p",
+ p_bcm_device->dev_wake_gpio);
+ if (IS_ERR(p_bcm_device->dev_wake_gpio)) {
+ ret = PTR_ERR(p_bcm_device->dev_wake_gpio);
+ if (ret != -ENOENT) {
+ dev_err(&pdev->dev,
+ "bcm_bt_uart_probe - dev_wake GPIO: %d\n", ret);
+ }
+ p_bcm_device->dev_wake_gpio = NULL;
+ } else {
+ int resume_flag;
+
+ p_bcm_device->dev_wake_active_low = gpiod_is_active_low
+ (p_bcm_device->dev_wake_gpio);
+ BT_DBG("bcm_bt_uart_probe - dev_wake a-low is %d (cans %d)",
+ p_bcm_device->dev_wake_active_low,
+ gpiod_cansleep(p_bcm_device->dev_wake_gpio));
+
+ /* configure dev_wake as output with init resumed state */
+ resume_flag = !p_bcm_device->dev_wake_active_low;
+ ret = gpiod_direction_output(p_bcm_device->dev_wake_gpio,
+ resume_flag);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "bcm_bt_uart_probe s dev_wake GPIO: %d\n", ret);
+ gpiod_put(p_bcm_device->dev_wake_gpio);
+ p_bcm_device->dev_wake_gpio = NULL;
+ goto end;
+ } else {
+ BT_DBG("bcm_bt_uart_probe - dev_wake set to %d",
+ resume_flag);
+ }
+ }
+
+ /* Get power on/off GPIO */
+ p_bcm_device->reg_on_gpio = gpiod_get(&pdev->dev, "reg-on");
+ BT_DBG("bcm_bt_uart_probe - gpiod_get for reg-on returned %p",
+ p_bcm_device->reg_on_gpio);
+ if (IS_ERR(p_bcm_device->reg_on_gpio)) {
+ ret = PTR_ERR(p_bcm_device->reg_on_gpio);
+ if (ret != -ENOENT) {
+ dev_err(&pdev->dev,
+ "bcm_bt_uart_probe - reg_on GPIO: %d\n", ret);
+ }
+ p_bcm_device->reg_on_gpio = NULL;
+ } else {
+ int poweron_flag;
+
+ p_bcm_device->reg_on_active_low = gpiod_is_active_low
+ (p_bcm_device->reg_on_gpio);
+ BT_DBG("bcm_bt_uart_probe - reg_on a-low is %d (cans %d)",
+ p_bcm_device->reg_on_active_low,
+ gpiod_cansleep(p_bcm_device->reg_on_gpio));
+
+ /* configure reg_on as output with init on state */
+ poweron_flag = !p_bcm_device->reg_on_active_low;
+ ret = gpiod_direction_output(p_bcm_device->reg_on_gpio,
+ poweron_flag);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "bcm_bt_uart_probe s reg_on GPIO: %d\n", ret);
+ gpiod_put(p_bcm_device->reg_on_gpio);
+ p_bcm_device->reg_on_gpio = NULL;
+ } else {
+ BT_DBG("bcm_bt_uart_probe - reg_on initially set to %d",
+ poweron_flag);
+ }
+ }
+
+ platform_set_drvdata(pdev, p_bcm_device);
+ /* Must be done before interrupt is requested */
+ INIT_WORK(&p_bcm_device->wakeup_work, bcm_bt_wakeup_task);
+
+ /* Get bt host wake GPIO */
+ p_bcm_device->bt_wake_gpio = gpiod_get(&pdev->dev, "bt-host-wake");
+ BT_DBG("bcm_bt_uart_probe - gpiod_get for bt-host-wake returned %p",
+ p_bcm_device->bt_wake_gpio);
+ if (IS_ERR(p_bcm_device->bt_wake_gpio)) {
+ ret = PTR_ERR(p_bcm_device->bt_wake_gpio);
+ if (ret != -ENOENT) {
+ dev_err(&pdev->dev,
+ "bcm_bt_uart_probe - bt_wake GPIO: %d\n", ret);
+ }
+ p_bcm_device->bt_wake_gpio = NULL;
+ } else {
+ /* configure bt_wake as input */
+ ret = gpiod_direction_input(p_bcm_device->bt_wake_gpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "bcm_bt_uart_probe s bt_wake GPIO: %d\n", ret);
+ gpiod_put(p_bcm_device->bt_wake_gpio);
+ p_bcm_device->bt_wake_gpio = NULL;
+ } else {
+ p_bcm_device->bt_wake_active_low = gpiod_is_active_low
+ (p_bcm_device->bt_wake_gpio);
+ BT_DBG("bcm_bt_uart_probe -bt_wake a-low is %d(cans%d)",
+ p_bcm_device->bt_wake_active_low,
+ gpiod_cansleep(p_bcm_device->bt_wake_gpio));
+ p_bcm_device->bt_wake_irq = gpiod_to_irq
+ (p_bcm_device->bt_wake_gpio);
+ if (p_bcm_device->bt_wake_irq < 0) {
+ dev_err(&pdev->dev,
+ "bcm_bt_uart_probe - HOST_WAKE IRQ: %d\n", ret);
+ } else {
+ unsigned long intflags = IRQF_TRIGGER_RISING;
+
+ if (p_bcm_device->bt_wake_active_low)
+ intflags = IRQF_TRIGGER_FALLING;
+
+ ret = request_irq(p_bcm_device->bt_wake_irq,
+ bcm_bt_uart_isr,
+ intflags, "bt_host_wake",
+ p_bcm_device);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "bcm_bt_uart_probe - failed to conf IRQ %d: %d",
+ p_bcm_device->bt_wake_irq, ret);
+ } else {
+ BT_DBG("bcm_bt_uart_probe - IRQ %d",
+ p_bcm_device->bt_wake_irq);
+ }
+ }
+ }
+ }
+
+ p_bcm_device->configure_sleep = 0;
+ if (!of_property_read_u32(np, "configure-sleep",
+ &p_bcm_device->configure_sleep)) {
+ BT_DBG("configure-sleep read as %d",
+ p_bcm_device->configure_sleep);
+ }
+ p_bcm_device->manual_fc = 0;
+ if (!of_property_read_u32(np, "manual-fc",
+ &p_bcm_device->manual_fc)) {
+ BT_DBG("manual-fc read as %d",
+ p_bcm_device->manual_fc);
+ }
+ p_bcm_device->baud_rate_before_config_download = 3000000;
+ if (!of_property_read_u32(
+ np, "baud-rate-before-config-download",
+ &p_bcm_device->baud_rate_before_config_download)) {
+ BT_DBG("baud-rate-before-config-download read as %d",
+ p_bcm_device->baud_rate_before_config_download);
+ }
+ p_bcm_device->configure_audio = 0;
+ if (!of_property_read_u32(np, "configure-audio",
+ &p_bcm_device->configure_audio)) {
+ BT_DBG("configure-audio read as %d",
+ p_bcm_device->configure_audio);
+ }
+ if (p_bcm_device->configure_audio) {
+ /* Defaults for audio */
+ p_bcm_device->PCMClockMode = 0;
+ p_bcm_device->PCMFillMethod = 2;
+ p_bcm_device->PCMFillNum = 0;
+ p_bcm_device->PCMFillValue = 3;
+ p_bcm_device->PCMInCallBitclock = 0;
+ p_bcm_device->PCMLSBFirst = 0;
+ p_bcm_device->PCMRightJustify = 0;
+ p_bcm_device->PCMRouting = 0;
+ p_bcm_device->PCMShortFrameSync = 0;
+ p_bcm_device->PCMSyncMode = 0;
+
+ if (!of_property_read_u32(np, "PCMClockMode",
+ &p_bcm_device->PCMClockMode))
+ BT_DBG("PCMClockMode read as %d",
+ p_bcm_device->PCMClockMode);
+ if (!of_property_read_u32(np, "PCMFillMethod",
+ &p_bcm_device->PCMFillMethod))
+ BT_DBG("PCMFillMethod readas %d",
+ p_bcm_device->PCMFillMethod);
+ if (!of_property_read_u32(np, "PCMFillNum",
+ &p_bcm_device->PCMFillNum))
+ BT_DBG("PCMFillNum read as %d",
+ p_bcm_device->PCMFillNum);
+ if (!of_property_read_u32(np, "PCMFillValue",
+ &p_bcm_device->PCMFillValue))
+ BT_DBG("PCMFillValue read as %d",
+ p_bcm_device->PCMFillValue);
+ if (!of_property_read_u32(np, "PCMInCallBitclock",
+ &p_bcm_device->PCMInCallBitclock))
+ BT_DBG("PCMInCallBitclock read as %d",
+ p_bcm_device->PCMInCallBitclock);
+ if (!of_property_read_u32(np, "PCMLSBFirst",
+ &p_bcm_device->PCMLSBFirst))
+ BT_DBG("PCMLSBFirst read as %d",
+ p_bcm_device->PCMLSBFirst);
+ if (!of_property_read_u32(np, "PCMRightJustify",
+ &p_bcm_device->PCMRightJustify))
+ BT_DBG("PCMRightJustify read as %d",
+ p_bcm_device->PCMRightJustify);
+ if (!of_property_read_u32(np, "PCMRouting",
+ &p_bcm_device->PCMRouting))
+ BT_DBG("PCMRouting read as %d",
+ p_bcm_device->PCMRouting);
+ if (!of_property_read_u32(np, "PCMShortFrameSync",
+ &p_bcm_device->PCMShortFrameSync))
+ BT_DBG("PCMShortFrameSync read as %d",
+ p_bcm_device->PCMShortFrameSync);
+ if (!of_property_read_u32(np, "PCMSyncMode",
+ &p_bcm_device->PCMSyncMode))
+ BT_DBG("PCMSyncMode read as %d",
+ p_bcm_device->PCMSyncMode);
+ }
+
+ if (!of_property_read_string(np, "tty", &tty_name)) {
+ strcpy(p_bcm_device->tty_name, tty_name);
+ BT_DBG("tty name read as %s", p_bcm_device->tty_name);
+ }
+
+ BT_DBG("idleTimeout set as %d", idleTimeout);
+
+ ret = 0; /* If we made it here, we're fine */
+
+ /* Place this instance on the device list */
+ spin_lock(&device_list_lock);
+ list_add_tail(&p_bcm_device->list, &device_list);
+ spin_unlock(&device_list_lock);
+
+end:
+ if (ret) {
+ if (p_bcm_device->reg_on_gpio) {
+ gpiod_put(p_bcm_device->reg_on_gpio);
+ p_bcm_device->reg_on_gpio = NULL;
+ }
+ if (p_bcm_device->bt_wake_gpio) {
+ gpiod_put(p_bcm_device->bt_wake_gpio);
+ p_bcm_device->bt_wake_gpio = NULL;
+ }
+ if (p_bcm_device->dev_wake_gpio) {
+ gpiod_put(p_bcm_device->dev_wake_gpio);
+ p_bcm_device->dev_wake_gpio = NULL;
+ }
+ }
+
+ BT_DBG("bcm_bt_uart_probe with the result %d", ret);
+ return ret;
+}
+
+/*
+ * Device instance removal
+ */
+static int bcm_bt_uart_remove(struct platform_device *pdev)
+{
+ struct bcm_device *p_bcm_device = platform_get_drvdata(pdev);
+
+ if (p_bcm_device == NULL) {
+ BT_DBG("bcm_bt_uart_remove - logic error, no probe?!");
+ return 0;
+ }
+
+ BT_DBG("bcm_bt_uart_remove %p context", p_bcm_device);
+
+ spin_lock(&device_list_lock);
+ list_del(&p_bcm_device->list);
+ spin_unlock(&device_list_lock);
+
+ BT_DBG("bcm_bt_uart_remove - freeing interrupt %d",
+ p_bcm_device->bt_wake_irq);
+ free_irq(p_bcm_device->bt_wake_irq, p_bcm_device);
+
+ if (p_bcm_device->reg_on_gpio) {
+ BT_DBG("bcm_bt_uart_remove - releasing reg_on_gpio");
+ gpiod_put(p_bcm_device->reg_on_gpio);
+ p_bcm_device->reg_on_gpio = NULL;
+ }
+
+ if (p_bcm_device->dev_wake_gpio) {
+ BT_DBG("bcm_bt_uart_remove - releasing dev_wake_gpio");
+ gpiod_put(p_bcm_device->dev_wake_gpio);
+ p_bcm_device->dev_wake_gpio = NULL;
+ }
+
+ if (p_bcm_device->bt_wake_gpio) {
+ BT_DBG("bcm_bt_uart_remove - releasing bt_wake_gpio");
+ gpiod_put(p_bcm_device->bt_wake_gpio);
+ p_bcm_device->bt_wake_gpio = NULL;
+ }
+
+ BT_DBG("bcm_bt_uart_remove %p done", p_bcm_device);
+ return 0;
+}
+
+/*
+ * Platform resume callback
+ */
+static int bcm_bt_uart_resume(struct device *pdev)
+{
+ int resume_flag;
+ struct bcm_device *p_bcm_device = platform_get_drvdata(
+ to_platform_device(pdev));
+
+ if (p_bcm_device == NULL) {
+ BT_DBG("bcm_bt_uart_resume - logic error, no device?!");
+ return 0;
+ }
+
+ BT_DBG("bcm_bt_uart_resume %p", p_bcm_device);
+
+ resume_flag = !p_bcm_device->dev_wake_active_low;
+ if (p_bcm_device->dev_wake_gpio) {
+ gpiod_set_value(p_bcm_device->dev_wake_gpio, resume_flag);
+ BT_DBG("bcm_bt_uart_resume: %d written, delaying 15 ms",
+ resume_flag);
+ mdelay(15);
+ }
+
+ /* Let the protocol know the platform is resuming */
+ if (p_bcm_device->protocol_callbacks.p_resume)
+ p_bcm_device->protocol_callbacks.p_resume(
+ p_bcm_device->protocol_callbacks.context);
+
+ return 0;
+}
+
+/*
+ * Platform suspend callback
+ */
+static int bcm_bt_uart_suspend(struct device *pdev)
+{
+ int resume_flag;
+ struct bcm_device *p_bcm_device = platform_get_drvdata(
+ to_platform_device(pdev));
+
+ if (p_bcm_device == NULL) {
+ BT_DBG("bcm_bt_uart_suspend - logic error, no device?!");
+ return 0;
+ }
+
+ BT_DBG("bcm_bt_uart_suspend %p", p_bcm_device);
+
+ /* Let the protocol know the platform is suspending */
+ if (p_bcm_device->protocol_callbacks.p_suspend)
+ p_bcm_device->protocol_callbacks.p_suspend(
+ p_bcm_device->protocol_callbacks.context);
+
+ /* Suspend the device */
+ if (p_bcm_device->dev_wake_gpio) {
+ resume_flag = !p_bcm_device->dev_wake_active_low;
+ gpiod_set_value(p_bcm_device->dev_wake_gpio, !resume_flag);
+ BT_DBG("bcm_bt_uart_suspend: %d written, delaying 15 ms",
+ !resume_flag);
+ mdelay(15);
+ }
+
+ return 0;
+}
+
+/*
+ * Entry point for calls from the protocol
+ */
+int btbcm_uart_control(int action, void *device_context,
+ void *p_data, unsigned long *p_size)
+{
+ struct btbcm_uart_callbacks *pc;
+ struct btbcm_uart_parameters *pp = p_data; /* for pars action only */
+ int ret = 0;
+ int resume_flag, poweron_flag;
+ struct bcm_device *p_bcm_device = device_context;
+ struct list_head *ptr;
+ bool is_found = false;
+
+ /* Special processing for the callback configuration */
+ if (action == BTBCM_UART_ACTION_CONFIGURE_CALLBACKS) {
+ pc = p_data;
+
+ BT_DBG("btbcm_uart_control - configure callbacks");
+ if ((p_data == NULL) || *p_size != sizeof(struct
+ btbcm_uart_callbacks) || (pc->interface_version !=
+ BTBCM_UART_INTERFACE_VERSION)) {
+ BT_DBG("btbcm_uart_control - callbacks mismatch!");
+ return -E2BIG;
+ }
+
+ BT_DBG("btbcm_uart_control - configure callbacks for %s(%p)",
+ pc->name, pc->context);
+ if (p_bcm_device == NULL) {
+ spin_lock(&device_list_lock);
+ list_for_each(ptr, &device_list) {
+ p_bcm_device = list_entry(ptr, struct
+ bcm_device, list);
+ if (!strcmp(p_bcm_device->tty_name, pc->name)) {
+ is_found = true;
+ break;
+ }
+ }
+
+ spin_unlock(&device_list_lock);
+ if (!is_found) {
+ BT_DBG("btbcm_uart_control - no device!");
+ return -ENOENT;
+ }
+ }
+
+ p_bcm_device->protocol_callbacks = *pc;
+ memcpy(p_data, &p_bcm_device, sizeof(p_bcm_device));
+ *p_size = sizeof(p_bcm_device);
+ return ret;
+ }
+
+ /* All other requests must have the right context */
+ if (p_bcm_device == NULL) {
+ BT_DBG("btbcm_uart_control - failing, no device");
+ return -ENOENT;
+ }
+
+ switch (action) {
+ case BTBCM_UART_ACTION_POWER_ON:
+ BT_DBG("btbcm_uart_control %p - power on", device_context);
+ if (p_bcm_device->reg_on_gpio) {
+ poweron_flag = !p_bcm_device->reg_on_active_low;
+ gpiod_set_value(p_bcm_device->reg_on_gpio,
+ poweron_flag);
+ BT_DBG("btbcm_uart_control - pwron %d, delay 15 ms",
+ poweron_flag);
+ mdelay(15);
+ }
+ break;
+
+ case BTBCM_UART_ACTION_POWER_OFF:
+ BT_DBG("btbcm_uart_control %p - power off", device_context);
+ if (p_bcm_device->reg_on_gpio) {
+ poweron_flag = p_bcm_device->reg_on_active_low;
+ gpiod_set_value(p_bcm_device->reg_on_gpio,
+ poweron_flag);
+ BT_DBG("btbcm_uart_control - pwroff %d, delay 15 ms",
+ poweron_flag);
+ mdelay(15);
+ }
+ break;
+
+ case BTBCM_UART_ACTION_RESUME:
+ BT_DBG("btbcm_uart_control %p - resume", device_context);
+ if (p_bcm_device->dev_wake_gpio) {
+ resume_flag = !p_bcm_device->dev_wake_active_low;
+ gpiod_set_value(p_bcm_device->dev_wake_gpio,
+ resume_flag);
+ BT_DBG("btbcm_uart_control - resume %d, delay 15 ms",
+ resume_flag);
+ mdelay(15);
+ }
+ break;
+
+ case BTBCM_UART_ACTION_SUSPEND:
+ BT_DBG("btbcm_uart_control %p - suspend", device_context);
+ if (p_bcm_device->dev_wake_gpio) {
+ resume_flag = !p_bcm_device->dev_wake_active_low;
+ gpiod_set_value(p_bcm_device->dev_wake_gpio,
+ !resume_flag);
+ BT_DBG("btbcm_uart_control - suspend %d, delay 15ms",
+ !resume_flag);
+ mdelay(15);
+ }
+ break;
+
+ case BTBCM_UART_ACTION_GET_PARAMETERS:
+ BT_DBG("btbcm_uart_control %p - get pars", device_context);
+ if ((p_data == NULL) ||
+ (*p_size < sizeof(struct btbcm_uart_parameters))) {
+ BT_DBG("btbcm_uart_control - failing, wrong par size");
+ return -E2BIG;
+ }
+
+ memset(pp, 0, sizeof(struct btbcm_uart_parameters));
+ pp->interface_version = BTBCM_UART_INTERFACE_VERSION;
+ pp->configure_sleep = p_bcm_device->configure_sleep;
+ pp->manual_fc = p_bcm_device->manual_fc;
+ pp->dev_wake_active_low = p_bcm_device->dev_wake_active_low;
+ pp->bt_wake_active_low = p_bcm_device->bt_wake_active_low;
+ pp->idle_timeout_in_secs = idleTimeout;
+ pp->baud_rate_before_config_download =
+ p_bcm_device->baud_rate_before_config_download;
+ pp->configure_audio = p_bcm_device->configure_audio;
+ pp->PCMClockMode = p_bcm_device->PCMClockMode;
+ pp->PCMFillMethod = p_bcm_device->PCMFillMethod;
+ pp->PCMFillNum = p_bcm_device->PCMFillNum;
+ pp->PCMFillValue = p_bcm_device->PCMFillValue;
+ pp->PCMInCallBitclock = p_bcm_device->PCMInCallBitclock;
+ pp->PCMLSBFirst = p_bcm_device->PCMLSBFirst;
+ pp->PCMRightJustify = p_bcm_device->PCMRightJustify;
+ pp->PCMRouting = p_bcm_device->PCMRouting;
+ pp->PCMShortFrameSync = p_bcm_device->PCMShortFrameSync;
+ pp->PCMSyncMode = p_bcm_device->PCMSyncMode;
+ *p_size = sizeof(struct btbcm_uart_parameters);
+ break;
+
+ default:
+ BT_DBG("btbcm_uart_control %p unknown act %d",
+ device_context, action);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(btbcm_uart_control);
+
+/* Platform susp and resume callbacks */
+static SIMPLE_DEV_PM_OPS(bcm_bt_uart_pm_ops,
+ bcm_bt_uart_suspend, bcm_bt_uart_resume);
+
+/* Driver match table */
+static const struct of_device_id bcm_bt_uart_match_table[] = {
+ { .compatible = "brcm,brcm-bt-uart" },
+ {}
+};
+
+/* Driver configuration */
+static struct platform_driver bcm_bt_uart_platform_driver = {
+ .probe = bcm_bt_uart_probe,
+ .remove = bcm_bt_uart_remove,
+ .driver = {
+ .name = "brcm_bt_uart",
+ .of_match_table = of_match_ptr(bcm_bt_uart_match_table),
+ .owner = THIS_MODULE,
+ .pm = &bcm_bt_uart_pm_ops,
+ },
+};
+
+module_platform_driver(bcm_bt_uart_platform_driver);
+
+MODULE_AUTHOR("Ilya Faenson");
+MODULE_DESCRIPTION("Broadcom Bluetooth UART Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
diff --git a/drivers/bluetooth/btbcm_uart.h b/drivers/bluetooth/btbcm_uart.h
new file mode 100644
index 0000000..5801753
--- /dev/null
+++ b/drivers/bluetooth/btbcm_uart.h
@@ -0,0 +1,90 @@
+/*
+ *
+ * Bluetooth BCM UART Driver Header
+ *
+ * Copyright (c) 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef BTBCM_UART_H
+#define BTBCM_UART_H
+
+/* Change the version if you change anything in this header */
+#define BTBCM_UART_INTERFACE_VERSION 1
+
+/* Callbacks from the driver into the protocol */
+typedef void (*p_suspend_callback)(void *context);
+typedef void (*p_resume_callback)(void *context);
+typedef void (*p_wakeup_callback)(void *context);
+struct btbcm_uart_callbacks {
+ int interface_version; /* interface # compiled against */
+ void *context; /* protocol instance context */
+ char name[64]; /* protocol tty device, for example, ttyS0 */
+
+ /* Callbacks proper */
+ p_suspend_callback p_suspend;
+ p_resume_callback p_resume;
+ p_wakeup_callback p_wakeup;
+};
+
+/* Driver parameters retrieved from the DT or ACPI */
+struct btbcm_uart_parameters {
+ int interface_version; /* interface # compiled against */
+
+ /* Parameters proper */
+ int configure_sleep;
+ int manual_fc;
+ int dev_wake_active_low;
+ int bt_wake_active_low;
+ int idle_timeout_in_secs;
+ int baud_rate_before_config_download;
+ int configure_audio;
+ int PCMClockMode;
+ int PCMFillMethod;
+ int PCMFillNum;
+ int PCMFillValue;
+ int PCMInCallBitclock;
+ int PCMLSBFirst;
+ int PCMRightJustify;
+ int PCMRouting;
+ int PCMShortFrameSync;
+ int PCMSyncMode;
+};
+
+/*
+ * Actions on the BTBCM_UART driver
+ */
+
+/* Configure protocol callbacks */
+#define BTBCM_UART_ACTION_CONFIGURE_CALLBACKS 0
+
+/* Retrieve BT device parameters */
+#define BTBCM_UART_ACTION_GET_PARAMETERS 1
+
+/* Resume the BT device via GPIO */
+#define BTBCM_UART_ACTION_RESUME 2
+
+/* Suspend the BT device via GPIO */
+#define BTBCM_UART_ACTION_SUSPEND 3
+
+/* Power the BT device off via GPIO */
+#define BTBCM_UART_ACTION_POWER_OFF 4
+
+/* Power the BT device on via GPIO */
+#define BTBCM_UART_ACTION_POWER_ON 5
+
+/* Execute an action on the BT device */
+extern int btbcm_uart_control(int action, void *device_context,
+ void *p_data, unsigned long *p_size);
+
+#endif
+
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index 1ec0b4a..e70f89b 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -1,8 +1,13 @@
/*
*
- * Bluetooth HCI UART driver for Broadcom devices
+ * Bluetooth UART H4 protocol for Broadcom devices
*
- * Copyright (C) 2015 Intel Corporation
+ * Copyright (c) 2015 Intel Corporation
+ * Copyright (c) 2015 Broadcom Corporation
+ *
+ * Acknowledgements:
+ * This file has been based on hci_h4.c originally developed
+ * by Maxim Krasnyansky and Marcel Holtmann.
*
*
* This program is free software; you can redistribute it and/or modify
@@ -15,139 +20,842 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/

+#include <linux/module.h>
+
#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
#include <linux/skbuff.h>

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>

-#include "btbcm.h"
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+
#include "hci_uart.h"
+#include "btbcm.h"
+#include "btbcm_uart.h"

-struct bcm_data {
- struct sk_buff *rx_skb;
+/* Protocol context */
+struct bcm_h4_struct {
struct sk_buff_head txq;
+ struct hci_uart *hu;
+
+ bool is_suspended; /* suspend/resume flag */
+
+ /* Recv data parsing is not used in normal operation */
+ bool parse_recv;
+ /* buffer includes the type */
+ unsigned char reassembly[1 + HCI_MAX_FRAME_SIZE];
+ unsigned int rsize;
+ u16 rexpected;
+
+ struct timer_list timer; /* idle timer */
+
+ struct btbcm_uart_parameters pars; /* device parameters */
+ void *device_context; /* ACPI/DT device context */
};

-static int bcm_open(struct hci_uart *hu)
+/* Static function prototypes for forward references */
+static void suspend_notification(void *context);
+static void resume_notification(void *context);
+static void wakeup_notification(void *context);
+static void bcm_ensure_wakeup(struct hci_uart *hu);
+
+/* Suspend/resume synchronization mutex */
+static DEFINE_MUTEX(plock);
+
+/*
+ * Idle timer callback
+ */
+static void bcm_idle_timeout(unsigned long arg)
{
- struct bcm_data *bcm;
+ struct hci_uart *hu = (struct hci_uart *)arg;
+ struct bcm_h4_struct *h4 = hu->priv;
+ int status;

- BT_DBG("hu %p", hu);
+ BT_DBG("bcm_idle_timeout hu %p", hu);
+
+ /* Suspend/resume operations are serialized */
+ mutex_lock(&plock);
+
+ if (!h4->is_suspended) {
+ /* Flow control the port if configured */
+ suspend_notification(hu);
+
+ /* Suspend the device */
+ status = btbcm_uart_control(BTBCM_UART_ACTION_SUSPEND,
+ h4->device_context, NULL, NULL);
+ if (status)
+ BT_DBG("bcm_idle_timeout failed to suspend device %d",
+ status);
+ }

- bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
- if (!bcm)
+ mutex_unlock(&plock);
+}
+
+/*
+ * Initialize protocol
+ */
+static int bcm_h4_open(struct hci_uart *hu)
+{
+ struct btbcm_uart_callbacks callbacks;
+ unsigned long callbacks_size = sizeof(callbacks);
+ int status;
+ struct bcm_h4_struct *h4;
+ struct tty_struct *tty = hu->tty;
+
+ BT_DBG("bcm_h4_open hu %p", hu);
+
+ h4 = kzalloc(sizeof(*h4), GFP_KERNEL);
+ if (!h4)
return -ENOMEM;

- skb_queue_head_init(&bcm->txq);
+ skb_queue_head_init(&h4->txq);
+ hu->priv = h4;
+ h4->hu = hu;
+ h4->is_suspended = false;
+ h4->parse_recv = false;
+ h4->rsize = 0;
+
+ /* Configure callbacks on the driver */
+ callbacks.interface_version = BTBCM_UART_INTERFACE_VERSION;
+ callbacks.context = hu;
+ strcpy(callbacks.name, tty->name);
+ callbacks.p_suspend = suspend_notification;
+ callbacks.p_resume = resume_notification;
+ callbacks.p_wakeup = wakeup_notification;
+ status = btbcm_uart_control(BTBCM_UART_ACTION_CONFIGURE_CALLBACKS,
+ NULL, &callbacks, &callbacks_size);
+ if (status) {
+ BT_DBG("bcm_h4_open failed to set driver callbacks %d", status);
+ return status;
+ }
+ if (callbacks_size != sizeof(void *)) {
+ BT_DBG("bcm_h4_open got back %d bytes from callbacks?!",
+ (int)callbacks_size);
+ return -EMSGSIZE;
+ }
+ memcpy(&h4->device_context, &callbacks, sizeof(void *));
+ BT_DBG("bcm_h4_open callbacks context %p", h4->device_context);
+
+ /* Retrieve device parameters */
+ callbacks_size = sizeof(h4->pars);
+ status = btbcm_uart_control(BTBCM_UART_ACTION_GET_PARAMETERS,
+ h4->device_context, &h4->pars,
+ &callbacks_size);
+ if (status) {
+ BT_DBG("bcm_h4_open failed to get dev parameters %d", status);
+ return status;
+ }
+ BT_DBG("Pars ver %d csleep %d dalow %d balow %d idle %d",
+ h4->pars.interface_version, h4->pars.configure_sleep,
+ h4->pars.dev_wake_active_low, h4->pars.bt_wake_active_low,
+ h4->pars.idle_timeout_in_secs);
+
+ /* Cycle power to make sure the device is in the known state */
+ status = btbcm_uart_control(BTBCM_UART_ACTION_POWER_OFF,
+ h4->device_context, NULL, NULL);
+ if (status) {
+ BT_DBG("bcm_h4_open failed to power off %d", status);
+ } else {
+ status = btbcm_uart_control(BTBCM_UART_ACTION_POWER_ON,
+ h4->device_context, NULL, NULL);
+ if (status)
+ BT_DBG("bcm_h4_open failed to power on %d", status);
+ }
+
+ /* Start the idle timer */
+ if (h4->pars.configure_sleep) {
+ setup_timer(&h4->timer, bcm_idle_timeout, (unsigned long)hu);
+ if (h4->pars.configure_sleep)
+ mod_timer(&h4->timer, jiffies + msecs_to_jiffies(
+ h4->pars.idle_timeout_in_secs * 1000));
+ }
+
+ return 0;
+}
+
+/*
+ * Flush protocol data
+ */
+static int bcm_h4_flush(struct hci_uart *hu)
+{
+ struct bcm_h4_struct *h4 = hu->priv;

- hu->priv = bcm;
+ BT_DBG("bcm_h4_flush hu %p", hu);
+
+ skb_queue_purge(&h4->txq);
return 0;
}

-static int bcm_close(struct hci_uart *hu)
+/*
+ * Make sure we're awake
+ * (called when the resumed state is required)
+ */
+static void bcm_ensure_wakeup(struct hci_uart *hu)
{
- struct bcm_data *bcm = hu->priv;
+ struct bcm_h4_struct *h4 = hu->priv;
+ int status;
+
+ if (!h4->pars.configure_sleep)
+ return;
+
+ /* Suspend/resume operations are serialized */
+ mutex_lock(&plock);
+
+ /* Nothing to do if resumed already */
+ if (!h4->is_suspended) {
+ mutex_unlock(&plock);

- BT_DBG("hu %p", hu);
+ /* Just reset the timer */
+ status = mod_timer(&h4->timer, jiffies + msecs_to_jiffies(
+ h4->pars.idle_timeout_in_secs * 1000));
+ return;
+ }
+
+ /* Wakeup the device */
+ status = btbcm_uart_control(BTBCM_UART_ACTION_RESUME,
+ h4->device_context, NULL, NULL);
+ if (status)
+ BT_DBG("bcm_ensure_wakeup failed to resume driver %d", status);

- skb_queue_purge(&bcm->txq);
- kfree_skb(bcm->rx_skb);
- kfree(bcm);
+ /* Unflow control the port if configured */
+ resume_notification(hu);
+
+ mutex_unlock(&plock);
+}
+
+/*
+ * Close protocol
+ */
+static int bcm_h4_close(struct hci_uart *hu)
+{
+ struct btbcm_uart_callbacks callbacks;
+ unsigned long callbacks_size = sizeof(callbacks);
+ struct bcm_h4_struct *h4 = hu->priv;
+ int status;

hu->priv = NULL;
+
+ BT_DBG("bcm_h4_close hu %p", hu);
+
+ /* If we're being closed, we must suspend */
+ if (h4->pars.configure_sleep) {
+ mutex_lock(&plock);
+
+ if (!h4->is_suspended) {
+ /* Flow control the port */
+ suspend_notification(hu);
+
+ /* Suspend the device */
+ status = btbcm_uart_control(BTBCM_UART_ACTION_SUSPEND,
+ h4->device_context, NULL,
+ NULL);
+ if (status) {
+ BT_DBG("bcm_h4_close suspend driver fail %d",
+ status);
+ }
+ }
+
+ mutex_unlock(&plock);
+
+ del_timer_sync(&h4->timer);
+ }
+
+ /* Power off the device if possible */
+ status = btbcm_uart_control(BTBCM_UART_ACTION_POWER_OFF,
+ h4->device_context, NULL, NULL);
+ if (status)
+ BT_DBG("bcm_h4_close failed to power off %d", status);
+
+ /* de-configure callbacks on the driver */
+ callbacks.interface_version = BTBCM_UART_INTERFACE_VERSION;
+ callbacks.context = hu;
+ callbacks.p_suspend = NULL;
+ callbacks.p_resume = NULL;
+ callbacks.p_wakeup = NULL;
+ status = btbcm_uart_control(BTBCM_UART_ACTION_CONFIGURE_CALLBACKS,
+ h4->device_context, &callbacks,
+ &callbacks_size);
+ if (status)
+ BT_DBG("bcm_h4_close failed to reset drv callbacks %d", status);
+ skb_queue_purge(&h4->txq);
+
+ hu->priv = NULL;
+ kfree(h4);
+
return 0;
}

-static int bcm_flush(struct hci_uart *hu)
+/*
+ * Enqueue frame for transmittion (padding, crc, etc)
+ */
+static int bcm_h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
- struct bcm_data *bcm = hu->priv;
+ struct bcm_h4_struct *h4 = hu->priv;
+
+ BT_DBG("bcm_h4_enqueue hu %p skb %p type %d len %d (%x %x %x)",
+ hu, skb, bt_cb(skb)->pkt_type, skb->len,
+ skb->data[0], skb->data[1], skb->data[2]);

- BT_DBG("hu %p", hu);
+ /* Make sure we're resumed */
+ bcm_ensure_wakeup(hu);

- skb_queue_purge(&bcm->txq);
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&h4->txq, skb);

return 0;
}

-static int bcm_setup(struct hci_uart *hu)
+/*
+ * HCI event processing if our own parsing of events is required
+ * NOTE: return 0 if the event is not to be passed up to BlueZ
+ */
+static bool bcm_process_hci_event(struct hci_uart *hu)
+{
+ struct bcm_h4_struct *h4 = hu->priv;
+ int status;
+ unsigned char evt_code = h4->reassembly[1];
+ unsigned char len = h4->reassembly[2];
+
+ BT_DBG("bcm_process_hci_event %02x event %02x len %02x %02x",
+ h4->reassembly[0], evt_code, len, h4->reassembly[3]);
+
+ /* switch (evt_code) { case HCI_EV_CMD_COMPLETE: break } */
+
+ status = hci_recv_stream_fragment(hu->hdev, h4->reassembly, h4->rsize);
+ if (status < 0)
+ BT_ERR("bcm_process_hci_event - reassembly failed %d", status);
+ return true;
+}
+
+/*
+ * ACL data processing if our own parsing of data is required
+ * NOTE: return 0 if the data is not to be passed up to BlueZ
+ */
+static bool bcm_process_acl_data(struct hci_uart *hu)
{
- BT_DBG("hu %p", hu);
+ struct bcm_h4_struct *h4 = hu->priv;
+ int ret;

- hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+ BT_DBG("bcm_process_acl_data %02x %02x %02x %02x %02x",
+ h4->reassembly[0], h4->reassembly[1], h4->reassembly[2],
+ h4->reassembly[3], h4->reassembly[4]);

- return btbcm_setup_patchram(hu->hdev);
+ ret = hci_recv_stream_fragment(hu->hdev, h4->reassembly, h4->rsize);
+ if (ret < 0)
+ BT_ERR("bcm_process_acl_data - Frame Reassembly Failed");
+ return true;
}

-static const struct h4_recv_pkt bcm_recv_pkts[] = {
- { H4_RECV_ACL, .recv = hci_recv_frame },
- { H4_RECV_SCO, .recv = hci_recv_frame },
- { H4_RECV_EVENT, .recv = hci_recv_frame },
-};
+/*
+ * Fragment parsing in the active filtering phase
+ * (not currently actively used)
+ */
+static void parse_fragment(struct hci_uart *hu, unsigned char *data, int count)
+{
+ struct bcm_h4_struct *h4 = hu->priv;
+
+ if (h4->rsize)
+ BT_DBG("parse_fragment type %x expected %d",
+ h4->reassembly[0], h4->rexpected);
+
+ while (count) {
+ if (!h4->rsize) {
+ /* Start of the frame */
+ h4->reassembly[0] = *data++;
+ h4->rsize++;
+ count--;
+ continue;
+ }
+
+ switch (h4->reassembly[0]) {
+ case HCI_EVENT_PKT:
+ if (h4->rsize == 1) {
+ /* event proper */
+ h4->reassembly[h4->rsize++] = *data++;
+ count--;
+ continue;
+ }
+ if (h4->rsize == 2) {
+ /* length */
+ h4->rexpected = *data;
+ h4->reassembly[h4->rsize++] = *data++;
+ count--;
+ BT_DBG("evthdr len %d, left %d", h4->rexpected, count);
+ continue;
+ }
+ if (count >= h4->rexpected) {
+ memcpy(&h4->reassembly[h4->rsize], data, h4->rexpected);
+ h4->rsize += h4->rexpected;
+ data += h4->rexpected;
+ count -= h4->rexpected;
+ bcm_process_hci_event(hu);
+ h4->rsize = 0; /* starting anew */
+ continue;
+ }
+ /* only piece of the event received */
+ memcpy(&h4->reassembly[h4->rsize], data, count);
+ h4->rsize += count;
+ data += count;
+ h4->rexpected -= count;
+ count = 0;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if ((h4->rsize == 1) || (h4->rsize == 2) || (h4->rsize == 3)) {
+ /* handle and first byte of length */
+ h4->reassembly[h4->rsize++] = *data++;
+ count--;
+ continue;
+ }
+ if (h4->rsize == 4) {
+ /* last byte of the length */
+ h4->reassembly[h4->rsize++] = *data++;
+ h4->rexpected = h4->reassembly[h4->rsize - 2] +
+ (h4->reassembly[h4->rsize - 1] << 8);
+ count--;
+ BT_DBG("dathdr len %d, left %d", h4->rexpected, count);
+ continue;
+ }
+ if (count >= h4->rexpected) {
+ memcpy(&h4->reassembly[h4->rsize], data, h4->rexpected);
+ h4->rsize += h4->rexpected;
+ data += h4->rexpected;
+ count -= h4->rexpected;
+ bcm_process_acl_data(hu);
+ h4->rsize = 0; /* starting anew */
+ continue;
+ }
+ /* only piece of data received */
+ memcpy(&h4->reassembly[h4->rsize], data, count);
+ h4->rsize += count;
+ data += count;
+ h4->rexpected -= count;
+ count = 0;
+ break;
+
+ default: /* Note that SCO may NOT come through the UART */
+ if (count >= 3)
+ BT_DBG("unexpected pkt type of %x (%02x %02x %02x)?!",
+ h4->reassembly[0], data[0], data[1], data[2]);
+ else if (count == 2)
+ BT_DBG("unexpected pkt type of %x (%02x %02x)?!",
+ h4->reassembly[0], data[0], data[1]);
+ else if (count == 1)
+ BT_DBG("unexpected pkt type of %x (%02x)?!",
+ h4->reassembly[0], data[0]);
+ else
+ BT_DBG("unexpected pkt type of %x?!",
+ h4->reassembly[0]);
+ h4->rsize = 0;
+ return;
+ }
+ }
+}

-static int bcm_recv(struct hci_uart *hu, const void *data, int count)
+/*
+ * Data indication from the line discipline
+ */
+static int bcm_h4_recv(struct hci_uart *hu, void *data, int count)
{
- struct bcm_data *bcm = hu->priv;
+ struct bcm_h4_struct *h4 = hu->priv;
+ int ret;

- if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+ BT_DBG("bcm_h4_recv hu %p len %d", hu, count);
+
+ if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) {
+ BT_DBG("h4_recv UART not registered!");
return -EUNATCH;
+ }

- bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
- bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
- if (IS_ERR(bcm->rx_skb)) {
- int err = PTR_ERR(bcm->rx_skb);
- BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
- return err;
+ /* Make sure we're resumed */
+ bcm_ensure_wakeup(hu);
+
+ /* If we're in the active phase, parse what we get */
+ if (h4->parse_recv) {
+ parse_fragment(hu, data, count);
+ } else {
+ ret = hci_recv_stream_fragment(hu->hdev, data, count);
+ if (ret < 0) {
+ BT_ERR("bcm_h4_recv: frame reassembly failed");
+ return ret;
+ }
}

return count;
}

-static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+/*
+ * Line discipline is grabbing a packet from the tx queue
+ */
+static struct sk_buff *bcm_h4_dequeue(struct hci_uart *hu)
{
- struct bcm_data *bcm = hu->priv;
+ struct bcm_h4_struct *h4 = hu->priv;
+ int is_qempty = skb_queue_empty(&h4->txq);

- BT_DBG("hu %p skb %p", hu, skb);
+ if (!is_qempty)
+ BT_DBG("bcm_h4_dequeue with non-empty queue");
+ return skb_dequeue(&h4->txq);
+}

- /* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
- skb_queue_tail(&bcm->txq, skb);
+/*
+ * Callbacks from the BCMBT_UART device
+ */

- return 0;
+/*
+ * The platform is suspending. Stop UART activity
+ */
+static void suspend_notification(void *context)
+{
+ struct ktermios ktermios;
+ struct hci_uart *hu = (struct hci_uart *)context;
+ struct bcm_h4_struct *h4 = hu->priv;
+ struct tty_struct *tty = hu->tty;
+ int status;
+ unsigned int set = 0;
+ unsigned int clear = 0;
+
+ BT_DBG("suspend_notification with is_suspended %d", h4->is_suspended);
+
+ if (!h4->pars.configure_sleep)
+ return;
+
+ if (!h4->is_suspended) {
+ if (h4->pars.manual_fc) {
+ /* Disable hardware flow control */
+ ktermios = tty->termios;
+ ktermios.c_cflag &= ~CRTSCTS;
+ status = tty_set_termios(tty, &ktermios);
+ if (status)
+ BT_DBG("suspend_notification dis fc fail %d",
+ status);
+ else
+ BT_DBG("suspend_notification hw fc disabled");
+
+ /* Clear RTS to prevent the device from sending */
+ /* (most PCs need OUT2 to enable interrupts) */
+ status = tty->driver->ops->tiocmget(tty);
+ BT_DBG("suspend_notification cur tiocm 0x%x", status);
+ set &= ~(TIOCM_OUT2 | TIOCM_RTS);
+ clear = ~set;
+ set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ status = tty->driver->ops->tiocmset(tty, set, clear);
+ if (status)
+ BT_DBG("suspend_notification clr RTS fail %d",
+ status);
+ else
+ BT_DBG("suspend_notification RTS cleared");
+ status = tty->driver->ops->tiocmget(tty);
+ BT_DBG("suspend_notification end tiocm 0x%x", status);
+ }
+
+ /* Once this callback returns, driver suspends BT via GPIO */
+ h4->is_suspended = true;
+ }
+}
+
+/*
+ * The platform is resuming. Resume UART activity.
+ */
+static void resume_notification(void *context)
+{
+ struct ktermios ktermios;
+ struct hci_uart *hu = (struct hci_uart *)context;
+ struct bcm_h4_struct *h4 = hu->priv;
+ struct tty_struct *tty = hu->tty;
+ int status;
+ unsigned int set = 0;
+ unsigned int clear = 0;
+
+ BT_DBG("resume_notification with is_suspended %d", h4->is_suspended);
+
+ if (!h4->pars.configure_sleep)
+ return;
+
+ /* When this callback executes, the device has woken up already */
+ if (h4->is_suspended) {
+ h4->is_suspended = false;
+
+ if (h4->pars.manual_fc) {
+ status = tty->driver->ops->tiocmget(tty);
+ BT_DBG("resume_notification cur tiocm 0x%x", status);
+ set |= (TIOCM_OUT2 | TIOCM_RTS);
+ clear = ~set;
+ set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ status = tty->driver->ops->tiocmset(tty, set, clear);
+ if (status)
+ BT_DBG("resume_notification set RTS fail %d",
+ status);
+ else
+ BT_DBG("resume_notification RTS set");
+
+ /* Re-enable hardware flow control */
+ ktermios = tty->termios;
+ ktermios.c_cflag |= CRTSCTS;
+ status = tty_set_termios(tty, &ktermios);
+ if (status)
+ BT_DBG("resume_notification enable fc fail %d",
+ status);
+ else
+ BT_DBG("resume_notification hw fc re-enabled");
+ }
+ }
+
+ /* If we're resumed, the idle timer must be running */
+ status = mod_timer(&h4->timer, jiffies +
+ msecs_to_jiffies(h4->pars.idle_timeout_in_secs * 1000));
}

-static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
+/*
+ * The BT device is resuming. Resume UART activity if suspended
+ */
+static void wakeup_notification(void *context)
{
- struct bcm_data *bcm = hu->priv;
+ struct ktermios ktermios;
+ struct hci_uart *hu = (struct hci_uart *)context;
+ struct bcm_h4_struct *h4 = hu->priv;
+ struct tty_struct *tty = hu->tty;
+ int status;
+ unsigned int set = 0;
+ unsigned int clear = 0;
+
+ if (!h4->pars.configure_sleep)
+ return;
+
+ status = tty->driver->ops->tiocmget(tty);
+ BT_DBG("wakeup_notification hu %p current tiocm 0x%x", hu, status);
+ if (h4->is_suspended) {
+ if (h4->pars.manual_fc) {
+ set |= (TIOCM_OUT2 | TIOCM_RTS);
+ clear = ~set;
+ set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ status = tty->driver->ops->tiocmset(tty, set, clear);
+ if (status)
+ BT_DBG("wakeup_notification set RTS fail %d",
+ status);
+ else
+ BT_DBG("wakeup_notification RTS set");
+
+ /* Re-enable hardware flow control */
+ ktermios = tty->termios;
+ ktermios.c_cflag |= CRTSCTS;
+ status = tty_set_termios(tty, &ktermios);
+ if (status)
+ BT_DBG("wakeup_notification fc-en failure %d",
+ status);
+ else
+ BT_DBG("wakeup_notification hw fc re-enabled");
+ }
+
+ h4->is_suspended = false;
+ }

- return skb_dequeue(&bcm->txq);
+ /* If we're resumed, the idle timer must be running */
+ status = mod_timer(&h4->timer, jiffies + msecs_to_jiffies(
+ h4->pars.idle_timeout_in_secs * 1000));
}

-static const struct hci_uart_proto bcm_proto = {
+/*
+ * Device setup that follows the protocol open
+ */
+static int bcm_h4_setup(struct hci_uart *hu)
+{
+ struct bcm_h4_struct *h4 = hu->priv;
+ int status;
+ struct sk_buff *skb;
+ unsigned char sleep_pars[] = {
+ 0x01, /* sleep mode 1=UART */
+ 0x02, /* idle threshold HOST (value * 300ms) */
+ 0x02, /* idle threshold HC (value * 300ms) */
+ 0x01, /* BT_WAKE active mode - 1=active high, 0 = active low */
+ 0x00, /* HOST_WAKE active mode - 1=active high, 0 = active low */
+ 0x01, /* Allow host sleep during SCO - FALSE */
+ 0x01, /* combine sleep mode and LPM - 1 == TRUE */
+ 0x00, /* enable tristate control of UART TX line - FALSE */
+ 0x00, /* USB auto-sleep on USB SUSPEND */
+ 0x00, /* USB USB RESUME timeout (seconds) */
+ 0x00, /* Pulsed Host Wake */
+ 0x00 /* Enable Break To Host */
+ };
+ unsigned char pcm_int_pars[] = {
+ 0x00, /* 0=PCM routing, 1=SCO over HCI */
+ 0x02, /* 0=128Kbps,1=256Kbps,2=512Kbps,3=1024Kbps,4=2048Kbps */
+ 0x00, /* short frame sync 0=short, 1=long */
+ 0x00, /* sync mode 0=slave, 1=master */
+ 0x00 /* clock mode 0=slave, 1=master */
+ };
+ unsigned char pcm_format_pars[] = {
+ 0x00, /* LSB_First 1=TRUE, 0=FALSE */
+ 0x00, /* Fill_Value (use 0-7 for fill bits value) */
+ 0x02, /* Fill_Method (2=sign extended) */
+ 0x03, /* Fill_Num # of fill bits 0-3) */
+ 0x01 /* Right_Justify 1=TRUE, 0=FALSE */
+ };
+ unsigned char time_slot_number = 0;
+
+ BT_DBG("bcm_h4_setup hu %p", hu);
+
+ /* Bring the UART into known default state */
+ status = btbcm_init_uart(hu);
+ if (status) {
+ BT_DBG("bcm_h4_setup failed to init BT device %d", status);
+ return status;
+ }
+
+ /* Basic sanity check */
+ skb = __hci_cmd_sync(hu->hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ status = PTR_ERR(skb);
+ BT_ERR("bcm_h4_setup HCI Reset failed (%d)", status);
+ return status;
+ }
+ kfree_skb(skb);
+ BT_DBG("bcm_h4_setup HCI Reset succeeded");
+
+ /* Set the new baud rate */
+ status = btbcm_set_baud_rate(hu,
+ h4->pars.baud_rate_before_config_download);
+ if (status) {
+ BT_ERR("bcm_h4_setup set_baud_rate faiilure %d", status);
+ return status;
+ }
+
+ hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+
+ /* Download the firmware and reconfigure the UART afterwards */
+ status = btbcm_setup_patchram(hu->hdev);
+ if (status) {
+ BT_ERR("bcm_h4_setup setup_patchram faiilure %d", status);
+ return status;
+ }
+
+ /* Configure SCO PCM parameters */
+ if (h4->pars.configure_audio) {
+ pcm_int_pars[0] = h4->pars.PCMRouting;
+ pcm_int_pars[1] = h4->pars.PCMInCallBitclock;
+ pcm_int_pars[2] = h4->pars.PCMShortFrameSync;
+ pcm_int_pars[3] = h4->pars.PCMSyncMode;
+ pcm_int_pars[4] = h4->pars.PCMClockMode;
+ skb = __hci_cmd_sync(hu->hdev, 0xfc1c, sizeof(pcm_int_pars),
+ pcm_int_pars, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ status = PTR_ERR(skb);
+ BT_ERR("bcm_h4_setup PCM INT VSC failed (%d)", status);
+ return status;
+ }
+ kfree_skb(skb);
+ BT_DBG("bcm_h4_setup PCM INT Parameters VSC succeeded");
+
+ pcm_format_pars[0] = h4->pars.PCMLSBFirst;
+ pcm_format_pars[1] = h4->pars.PCMFillValue;
+ pcm_format_pars[2] = h4->pars.PCMFillMethod;
+ pcm_format_pars[3] = h4->pars.PCMFillNum;
+ pcm_format_pars[4] = h4->pars.PCMRightJustify;
+ skb = __hci_cmd_sync(hu->hdev, 0xfc1e, sizeof(pcm_format_pars),
+ pcm_format_pars, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ status = PTR_ERR(skb);
+ BT_ERR("bcm_h4_setup PCM Format VSC failed (%d)",
+ status);
+ return status;
+ }
+ kfree_skb(skb);
+ BT_DBG("bcm_h4_setup PCM Format VSC succeeded");
+
+ skb = __hci_cmd_sync(hu->hdev, 0xfc22, sizeof(time_slot_number),
+ &time_slot_number, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ status = PTR_ERR(skb);
+ BT_ERR("bcm_h4_setup SCO Time Slot VSC failed (%d)",
+ status);
+ return status;
+ }
+ kfree_skb(skb);
+ BT_DBG("bcm_h4_setup SCO Time Slot VSC succeeded");
+ }
+
+ /* Configure device's suspend/resume operation */
+ if (h4->pars.configure_sleep) {
+ /* Override the default */
+ sleep_pars[3] = (unsigned char)!h4->pars.bt_wake_active_low;
+ sleep_pars[4] = (unsigned char)!h4->pars.dev_wake_active_low;
+ skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_pars),
+ sleep_pars, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ status = PTR_ERR(skb);
+ BT_ERR("bcm_h4_setup Sleep VSC failed (%d)", status);
+ return status;
+ }
+ kfree_skb(skb);
+ BT_DBG("bcm_h4_setup Set Sleep Parameters VSC succeeded");
+ }
+
+ return 0;
+}
+
+/*
+ * Protocol callbacks
+ */
+static struct hci_uart_proto h4p = {
.id = HCI_UART_BCM,
- .name = "BCM",
- .open = bcm_open,
- .close = bcm_close,
- .flush = bcm_flush,
- .setup = bcm_setup,
- .recv = bcm_recv,
- .enqueue = bcm_enqueue,
- .dequeue = bcm_dequeue,
+ .name = "UARTBCM",
+ .open = bcm_h4_open,
+ .close = bcm_h4_close,
+ .flush = bcm_h4_flush,
+ .setup = bcm_h4_setup,
+ .recv = bcm_h4_recv,
+ .enqueue = bcm_h4_enqueue,
+ .dequeue = bcm_h4_dequeue,
};

+/*
+ * Protocol init
+ */
int __init bcm_init(void)
{
- return hci_uart_register_proto(&bcm_proto);
+ int status = hci_uart_register_proto(&h4p);
+
+ if (!status)
+ BT_INFO("Broadcom H4 protocol initialized");
+ else
+ BT_ERR("Broadcom H4 protocol registration failed %d", status);
+
+ return status;
}

+/*
+ * Protocol shutdown
+ */
int __exit bcm_deinit(void)
{
- return hci_uart_unregister_proto(&bcm_proto);
+ BT_INFO("Broadcom H4 protocol de-initialized");
+ return hci_uart_unregister_proto(&h4p);
}
+
--
1.9.1


2015-05-07 20:25:28

by Ilya Faenson

[permalink] [raw]
Subject: [RFC 1/2] Bluetooth UART Device Tree bindings

Device Tree bindings to configure the Bluetooth UART device.

Signed-off-by: Ilya Faenson <[email protected]>
---
.../devicetree/bindings/net/bluetooth/btbcm.txt | 54 ++++++++++++++++++++++
1 file changed, 54 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/bluetooth/btbcm.txt

diff --git a/Documentation/devicetree/bindings/net/bluetooth/btbcm.txt b/Documentation/devicetree/bindings/net/bluetooth/btbcm.txt
new file mode 100644
index 0000000..cc9f225
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/btbcm.txt
@@ -0,0 +1,54 @@
+btbcm
+------
+
+Required properties:
+
+ - compatible : must be "brcm,brcm-bt-uart".
+ - tty : tty device connected to this Bluetooth device.
+
+Optional properties:
+
+ - bt-host-wake-gpios : bt-host-wake input GPIO to be used as an interrupt.
+
+ - bt-wake-gpios : bt-wake output GPIO to be used to suspend / resume device.
+
+ - reg-on-gpios : reg-on output GPIO to be used to power device on/off.
+
+ - baud-rate-before-config-download : initial Bluetooth device baud rate.
+ Default: 3000000.
+
+ - manual-fc : flow control UART in suspend / resume scenarios.
+ Default: 0.
+
+ - configure-sleep : configure suspend / resume flag.
+ Default: 0.
+
+ - configure-audio : configure platform PCM SCO flag.
+ Default: 0.
+
+ - PCM* : SCO PCM platform parameters. Work with Broadcom on setting.
+ Defaults: see the example below.
+
+
+Example:
+
+ bcm4354_bt_uart: bcm4354-bt-uart {
+ compatible = "bcm-bt-uart,bcm4354-bt-uart";
+ bt-wake-gpios = <&gpio4 30 GPIO_ACTIVE_HIGH>;
+ bt-host-wake-gpios = <&gpio4 31 GPIO_ACTIVE_HIGH>;
+ tty = "ttyS0";
+ baud-rate-before-config-download = <3000000>;
+ configure-sleep = <1>;
+ configure-audio = <1>;
+ PCMClockMode = <0>;
+ PCMFillMethod = <2>;
+ PCMFillNum = <0>;
+ PCMFillValue = <3>;
+ PCMInCallBitclock = <0>;
+ PCMLSBFirst = <0>;
+ PCMRightJustify = <0>;
+ PCMRouting = <0>;
+ PCMShortFrameSync = <0>;
+ PCMSyncMode = <0>;
+ };
+
--
1.9.1