123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- ' Copyright (c) .NET Foundation and Contributors
- ' Copyright (c) 2019 Bruce A Henderson
- '
- ' All rights reserved.
- '
- ' Permission is hereby granted, free of charge, to any person obtaining a copy
- ' of this software and associated documentation files (the "Software"), to deal
- ' in the Software without restriction, including without limitation the rights
- ' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- ' copies of the Software, and to permit persons to whom the Software is
- ' furnished to do so, subject to the following conditions:
- '
- ' The above copyright notice and this permission notice shall be included in all
- ' copies or substantial portions of the Software.
- '
- ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- ' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- ' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- ' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- ' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- ' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- ' SOFTWARE.
- '
- SuperStrict
- Import brl.threadpool
- Import "../gpiodriver.bmx"
- Import "libgpiod.bmx"
- Type TLibGpiodDriver Extends TGpioDriver
- Field chipPtr:Byte Ptr
-
- Field pinNumberToLines:TGpiodLine[]
- Field pinNumberToEventHandler:TLibGpiodDriverEventHandler[]
-
- Field pool:TThreadPoolExecutor
- Private
- Method New()
- End Method
- Public
- Method New(gpioChip:UInt = 0)
- If Not LibGpiodAvailable() Then
- Throw New TPlatformNotSupportedException()
- End If
-
- chipPtr = gpiod_chip_open_by_number(gpioChip)
- If Not chipPtr Then
- Throw New TIOException("no chip found")
- End If
-
- pool = TThreadPoolExecutor.newCachedThreadPool()
-
- Local count:Int = PinCount()
- pinNumberToLines = New TGpiodLine[count]
- pinNumberToEventHandler = New TLibGpiodDriverEventHandler[count]
- End Method
- Method PinCount:Int()
- Return gpiod_chip_num_lines(chipPtr)
- End Method
-
- Method ConvertPinNumberToLogicalNumberingScheme:Int(pinNumber:Int)
- ' not supported
- End Method
-
- Method OpenPin(pinNumber:Int)
- Local pin:TGpiodLine = TGpiodLine._create(gpiod_chip_get_line(chipPtr, UInt(pinNumber)))
- If Not pin Then
- Throw New TIOException("Error opening pin")
- End If
-
- pinNumberToLines[pinNumber] = pin
- End Method
-
- Method ClosePin(pinNumber:Int)
- Local pin:TGpiodLine = pinNumberToLines[pinNumber]
-
- If pin And Not IsListeningEvent(pinNumber) Then
- pin.Dispose()
- pinNumberToLines[pinNumber] = Null
- End If
-
- End Method
-
- Method SetPinMode(pinNumber:Int, pinMode:EPinMode)
- Local requestResult:Int = -1
-
- Local pin:TGpiodLine = pinNumberToLines[pinNumber]
- If pin Then
- If pinMode = EPinMode.Input
- requestResult = pin.RequestInput(String(pinNumber))
- Else
- requestResult = pin.RequestOutput(String(pinNumber))
- End If
- pin.SetPinMode(pinMode)
- End If
-
- If requestResult = -1 Then
- Throw New TIOException("Error setting pin mode : " + pinNumber)
- End If
- End Method
-
- Method GetPinMode:EPinMode(pinNumber:Int)
- Local pin:TGpiodLine = pinNumberToLines[pinNumber]
- If Not pin Then
- Throw New TInvalidOperationException("Pin not open : " + pinNumber)
- End If
-
- Return pin.GetPinMode()
- End Method
-
- Method IsListeningEvent:Int(pinNumber:Int)
- Return pinNumberToEventHandler[pinNumber] <> Null
- End Method
-
- Method IsPinModeSupported:Int(pinNumber:Int, pinMode:EPinMode)
- ' Libgpiod Api do not support pull up or pull down resistors for now.
- Return pinMode <> EPinMode.InputPullDown And pinMode <> EPinMode.InputPullUp
- End Method
-
- Method Read:EPinValue(pinNumber:Int)
- Local pin:TGpiodLine = pinNumberToLines[pinNumber]
- If pin Then
- Local pinValue:EPinValue
- Local result:Int = pin.GetValue()
- If result = -1 Or Not EPinValue.TryConvert(result, pinValue) Then
- Throw New TIOException("Read pin error : " + pinNumber)
- End If
- Return pinValue
- Else
- Throw New TInvalidOperationException("Pin not opened : " + pinNumber)
- End If
- End Method
-
- Method Write(pinNumber:Int, value:EPinValue)
- Local pin:TGpiodLine = pinNumberToLines[pinNumber]
- If Not pin Then
- Throw New TIOException("Pin not opened : " + pinNumber)
- End If
-
- If value = EPinValue.High Then
- pin.SetValue(1)
- Else
- pin.SetValue(0)
- End If
- End Method
-
- Method AddCallbackForPinValueChangedEvent(pinNumber:Int, eventTypes:EPinEventTypes, context:Object, callback(context:Object, sender:Object, pinValueChangedEventArgs:SPinValueChangedEventArgs))
- If eventTypes & EPinEventTypes.Rising Or eventTypes & EPinEventTypes.Falling Then
- Local eventHandler:TLibGpiodDriverEventHandler = pinNumberToEventHandler[pinNumber]
- If Not eventHandler Then
- eventHandler = PopulateEventHandler(pinNumber)
- End If
-
- If eventTypes & EPinEventTypes.Rising Then
- eventHandler.valueRising.AddLast(context, callback)
- End If
-
- If eventTypes & EPinEventTypes.Falling Then
- eventHandler.valueFalling.AddLast(context, callback)
- End If
- Else
- Throw New TIOException("Invalid event type")
- End If
- End Method
-
- Method PopulateEventHandler:TLibGpiodDriverEventHandler(pinNumber:Int)
- Local pin:TGpiodLine = pinNumberToLines[pinNumber]
- If pin Then
- If Not pin.IsFree() Then
- pin.Dispose()
- pin = TGpiodLine._create(gpiod_chip_get_line(chipPtr, UInt(pinNumber)))
- pinNumberToLines[pinNumber] = pin
- EndIf
- End If
-
- Return New TLibGpiodDriverEventHandler(pinNumber, pin, pool)
- End Method
-
- Method RemoveCallbackForPinValueChangedEvent(pinNumber:Int, callback(context:Object, sender:Object, pinValueChangedEventArgs:SPinValueChangedEventArgs))
- Local eventHandler:TLibGpiodDriverEventHandler = pinNumberToEventHandler[pinNumber]
- If eventHandler Then
- eventHandler.valueFalling.Remove(callback)
- eventHandler.ValueRising.Remove(callback)
-
- If eventHandler.IsCallbackListEmpty() Then
- pinNumberToEventHandler[pinNumber] = Null
- eventHandler.Dispose()
- End If
- Else
- Throw New TIOException("Not listening for event")
- End If
- End Method
-
- Method WaitForEvent()
- End Method
- Method Dispose()
- If pinNumberToEventHandler Then
- For Local i:Int = 0 Until pinNumberToEventHandler.length
- Local handler:TLibGpiodDriverEventHandler = pinNumberToEventHandler[i]
- If handler Then
- handler.Dispose()
- pinNumberToEventHandler[i] = Null
- End If
- Next
- pinNumberToEventHandler = Null
- End If
-
- If pinNumberToLines Then
- For Local i:Int = 0 Until pinNumberToLines.length
- Local pin:TGpiodLine = pinNumberToLines[i]
- If pin Then
- pin.Dispose()
- pinNumberToLines[i] = Null
- End If
- Next
- pinNumberToLines = Null
- End If
-
- If chipPtr Then
- gpiod_chip_close(chipPtr)
- chipPtr = Null
- End If
-
- End Method
- End Type
- Type TGpiodLine
- Field linePtr:Byte Ptr
- Field pinMode:EPinMode
-
- Function _create:TGpiodLine(linePtr:Byte Ptr)
- If linePtr Then
- Local this:TGpiodLine = New TGpiodLine
- this.linePtr = linePtr
- Return this
- End If
- End Function
-
- Method GetPinMode:EPinMode()
- Return pinMode
- End Method
-
- Method SetPinMode(value:EPinMode)
- pinMode = value
- End Method
-
- Method GetValue:Int()
- Return gpiod_line_get_value(linePtr)
- End Method
-
- Method SetValue:Int(value:Int)
- Return gpiod_line_set_value(linePtr, value)
- End Method
-
- Method RequestInput:Int(consumer:String)
- Return gpiod_line_request_input(linePtr, consumer)
- End Method
-
- Method RequestOutput:Int(consumer:String)
- Return gpiod_line_request_output(linePtr, consumer, 0)
- End Method
-
- Method RequestBothEdgesEvents:Int(consumer:String)
- Return gpiod_line_request_both_edges_events(linePtr, consumer)
- End Method
- Rem
- bbdoc: Waits for an event on the GPIO line.
- returns: 0 if wait timed out, -1 if an error occurred, 1 if an event occurred.
- End Rem
- Method EventWait:Int(timespec:STimeSpec Var)
- Return gpiod_line_event_wait(linePtr, timespec)
- End Method
-
- Rem
- bbdoc: Reads the last event from the GPIO line.
- returns: 0 if the event was read correctly, -1 on error.
- End Rem
- Method EventRead:Int(event:SGpioLineEvent Var)
- Return gpiod_line_event_read(linePtr, event)
- End Method
-
- Method IsFree:Int()
- Return gpiod_line_is_free(linePtr)
- End Method
- Method Dispose()
- If linePtr Then
- gpiod_line_release(linePtr)
- linePtr = Null
- End If
- End Method
- Method Delete()
- Dispose()
- End Method
-
- End Type
- Type TLibGpiodDriverEventHandler
- Field pool:TThreadPoolExecutor
-
- ' PinChangeEventHandler(object sender, PinValueChangedEventArgs pinValueChangedEventArgs
- Field valueRising:TCallbackList = New TCallbackList
- Field valueFalling:TCallbackList = New TCallbackList
-
- Field pinNumber:Int
-
- Field _shutdown:Int
-
- Method New(pinNumber:Int, pin:TGpiodLine, pool:TThreadPoolExecutor)
- Self.pool = pool
-
- Self.pinNumber = pinNumber
- ' CancellationTokenSource = new CancellationTokenSource ' TODO
- SubscribeForEvent(pin)
-
- InitializeEventDetectionTask(pin)
- End Method
-
- Method SubscribeForEvent(pin:TGpiodLine)
- Local eventSuccess:Int = pin.RequestBothEdgesEvents("Listen " + pinNumber + " for both edge event")
- If eventSuccess < 0 Then
- Throw New TIOException("Request event error : " + pinNumber)
- End If
- End Method
-
- Method InitializeEventDetectionTask(pin:TGpiodLine)
-
- pool.execute(New TEventDetectionTask(Self, pinNumber, pin))
-
- End Method
-
- Method OnPinValueChanged(args:SPinValueChangedEventArgs, detectionOfEventTypes:EPinEventTypes)
- If detectionOfEventTypes = EPinEventTypes.Rising And args.changeType = EPinEventTypes.Rising Then
- If valueRising.size Then
- For Local i:Int = 0 Until valueRising.size
- Local cb:TCallback = valueRising.data[i]
- cb.data(cb.context, Self, args)
- Next
- End If
- Else
- If valueFalling.size Then
- For Local i:Int = 0 Until valueFalling.size
- Local cb:TCallback = valueFalling.data[i]
- cb.data(cb.context, Self, args)
- Next
- End If
- End If
- End Method
- Method IsCallbackListEmpty:Int()
- Return Not valueRising.size And Not valueFalling.size
- End Method
-
- Method Dispose()
- _shutdown = True
- End Method
-
- End Type
- Type TEventDetectionTask Extends TRunnable
- Field eventHandler:TLibGpiodDriverEventHandler
- Field pinNumber:Int
- Field pin:TGpiodLine
-
- Field timespec:STimeSpec
-
- Method New(eventHandler:TLibGpiodDriverEventHandler, pinNumber:Int, pin:TGpiodLine)
- Self.eventHandler = eventHandler
- Self.pinNumber = pinNumber
- Self.pin = pin
-
- timespec = New STimeSpec(0, 1000000)
- End Method
-
- Method Run()
-
- While Not eventHandler._shutdown
-
- Local res:Int = pin.EventWait(timespec)
-
- ' error
- If res = -1 Then
- Throw New TIOException("Event wait error " + pinNumber)
- End If
-
- ' event
- If res = 1 Then
-
- Local event:SGpioLineEvent
-
- If pin.EventRead(event) = -1 Then
- Throw New TIOException("Event read error " + pinNumber)
- End If
-
- eventHandler.OnPinValueChanged(New SPinValueChangedEventArgs(event.AsPinEventType(), pinNumber), event.AsPinEventType())
-
- End If
-
- Wend
-
- End Method
-
- End Type
- Type TCallback
- Field context:Object
- Field data(context:Object, sender:Object, pinValueChangedEventArgs:SPinValueChangedEventArgs)
-
- Method New(context:Object, data(context:Object, sender:Object, pinValueChangedEventArgs:SPinValueChangedEventArgs))
- Self.context = context
- Self.data = data
- End Method
- End Type
- Type TCallbackList
- Field data:TCallback[16]
- Field size:Int
- Method _ensureCapacity(newSize:Int)
- If newSize >= data.length Then
- data = data[.. newSize * 3 / 2 + 1]
- End If
- End Method
- Method Clear()
- For Local i:Int = 0 Until size
- data[i] = Null
- Next
- size = 0
- End Method
- Method IsEmpty:Int()
- Return size = 0
- End Method
-
- Method AddLast(context:Object, callback(context:Object, sender:Object, pinValueChangedEventArgs:SPinValueChangedEventArgs))
- _ensureCapacity(size + 1)
- data[size] = New TCallback(context, callback)
- size :+ 1
- End Method
-
- Method Remove(callback(context:Object, sender:Object, pinValueChangedEventArgs:SPinValueChangedEventArgs))
- If size Then
- Local offset:Int = -1
- For Local i:Int = 0 Until size
- If data[i].data = callback Then
- offset = i
- End If
- Next
-
- If offset >= 0 Then
- Local length:Int = size - offset
- If length > 0 Then
- ArrayCopy(data, offset + 1, data, offset, length)
- End If
- size :- 1
- data[size] = Null
- End If
- End If
- End Method
-
- Method _removeAt(index:Int)
- data[index] = Null
- End Method
-
- End Type
|