<commit_before>/*
   SwiftyGPIO

   Copyright (c) 2016 <NAME>
   Licensed under the MIT license, as follows:

   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.)
*/
#if os(Linux)
    import Glibc
#else
    import Darwin.C
#endif
import Foundation

extension SwiftyGPIO {

    public static func hardwarePWMs(for board: SupportedBoard) -> [Int:[GPIOName:PWMOutput]]? {
        switch board {
        case .RaspberryPiRev1:
            fallthrough
        case .RaspberryPiRev2:
            fallthrough
        case .RaspberryPiPlusZero:
            return PWMRPI1
        case .RaspberryPi2:
            fallthrough
        case .RaspberryPi3:
            return PWMRPI23
        default:
            return nil
        }
    }
}

// MARK: - SPI Presets
extension SwiftyGPIO {

    // RaspberryPis ARMv6 (all 1, Zero, Zero W) PWMs, only accessible ones, divided in channels (can use only one for each channel)
    static let PWMRPI1: [Int:[GPIOName:PWMOutput]] = [
        0: [.P12: RaspberryPWM(gpioId: 12, alt: 0, channel:0, baseAddr: 0x20000000), .P18: RaspberryPWM(gpioId: 18, alt: 5, channel:0, baseAddr: 0x20000000)],
        1: [.P13: RaspberryPWM(gpioId: 13, alt: 0, channel:1, baseAddr: 0x20000000), .P19: RaspberryPWM(gpioId: 19, alt: 5, channel:1, baseAddr: 0x20000000)]
    ]

    // RaspberryPis ARMv7 (2-3) PWMs, only accessible ones, divided in channels (can use only one for each channel)
    static let PWMRPI23: [Int:[GPIOName:PWMOutput]] = [
        0: [.P12: RaspberryPWM(gpioId: 12, alt: 0, channel:0, baseAddr: 0x3F000000), .P18: RaspberryPWM(gpioId: 18, alt: 5, channel:0, baseAddr: 0x3F000000)],
        1: [.P13: RaspberryPWM(gpioId: 13, alt: 0, channel:1, baseAddr: 0x3F000000), .P19: RaspberryPWM(gpioId: 19, alt: 5, channel:1, baseAddr: 0x3F000000)]
    ]
}

// MARK: PWM

public protocol PWMOutput {
    func initPWM()
    func startPWM(period ns: Int, duty percent: Int)
    func stopPWM()

    func initPWMPattern(bytes count: Int, at frequency: Int, with resetDelay: Int, dutyzero: Int, dutyone: Int)
    func sendDataWithPattern(values: [UInt8]) 
    func waitOnSendData()
    func cleanupPattern() 
}

public class RaspberryPWM: PWMOutput {
    let gpioId: UInt
    let alt: UInt
    let channel: Int

    let BCM2708_PERI_BASE: Int
    let GPIO_BASE: Int  // GPIO Register
    let PWM_BASE: Int   // PWM Register
    let CLOCK_BASE: Int // Clock Manager Register
    let BCM2708_PHY_BASE: Int = 0x7e000000
    let PWM_PHY_BASE: Int
    let PWMDMA = 5

    var gpioBasePointer: UnsafeMutablePointer<UInt>!
    var pwmBasePointer: UnsafeMutablePointer<UInt>!
    var clockBasePointer: UnsafeMutablePointer<UInt>!
    var dmaBasePointer: UnsafeMutablePointer<UInt>!
    var dmaCallbackPointer: UnsafeMutablePointer<DMACallback>! = nil
    var pwmRawPointer: UnsafeMutablePointer<UInt>! = nil
    var mailbox: MailBox! = nil

    var zeroPattern: Int = 0
    var onePattern: Int = 0
    var symbolBits: Int = 0
    var patternFrequency: Int = 0
    var patternDelay: Int = 0
    var dataLength: Int = 0

    public init(gpioId: UInt, alt: UInt, channel: Int, baseAddr: Int, dmanum: Int = 5) { //PWMDMA
        self.gpioId = gpioId
        self.alt = alt
        self.channel = channel
        BCM2708_PERI_BASE = baseAddr
        GPIO_BASE = BCM2708_PERI_BASE + 0x200000  // GPIO Register
        PWM_BASE =  BCM2708_PERI_BASE + 0x20C000  // PWM Register
        CLOCK_BASE = BCM2708_PERI_BASE + 0x101000 // Clock Manager Register
        PWM_PHY_BASE = BCM2708_PHY_BASE + 0x20C000 // PWM controller physical address
    }

    /// Init PWM on this pin, set alternative function
    public func initPWM() {
        var mem_fd: Int32 = 0

        //The only mem device that support PWM is /dev/mem
        mem_fd=open("/dev/mem", O_RDWR | O_SYNC)

        guard mem_fd > 0 else {
            fatalError("Can't open /dev/mem , use sudo!")
        }

        gpioBasePointer = memmap(from: mem_fd, at: GPIO_BASE)
        pwmBasePointer = memmap(from: mem_fd, at: PWM_BASE)
        clockBasePointer = memmap(from: mem_fd, at: CLOCK_BASE)

        let DMAOffsets: [Int] = [0x00007000, 0x00007100, 0x00007200, 0x00007300,
                         0x00007400, 0x00007500, 0x00007600, 0x00007700,
                         0x00007800, 0x00007900, 0x00007a00, 0x00007b00,
                         0x00007c00, 0x00007d00, 0x00007e00, 0x00e05000]

        func dmanumToPhysicalAddress(_ dmanum: Int) -> Int {
            guard dmanum < DMAOffsets.count else { return 0 }
            return BCM2708_PERI_BASE + DMAOffsets[dmanum]
        }

        var dma_addr = dmanumToPhysicalAddress(PWMDMA) // Address of a specific DMA Channel registers set
        let pageOffset = dma_addr % PAGE_SIZE
        dma_addr -= pageOffset

        let dma_map = UnsafeMutableRawPointer(memmap(from: mem_fd, at: dma_addr))
        dmaBasePointer = (dma_map + pageOffset).assumingMemoryBound(to: UInt.self)

        close(mem_fd)

        // set PWM alternate function for this GPIO
        setAlt()
    }

    /// Start a PWM signal with specific period in ns and duty cycle from 0 to 100.
    /// The signal starts, asynchronously(manged by a device external to the CPU), once this method is called and 
    /// needs to be stopped manually calling `stopPWM()`.
    public func startPWM(period ns: Int, duty percent: Int) {
        // Kill the clock
        clockBasePointer.advanced(by: 40).pointee = CLKM_PASSWD | CLKM_CTL_KILL     //CM CTL register: Set KILL flag
        usleep(10)

        // If the required frequency is too high, this value reduces the number of samples (scale does the opposite)
        let highFreqSampleReduction: UInt = (ns < 750) ? 10 : 1

        let freq: UInt = (1_000_000_000/UInt(ns)) * 100 / highFreqSampleReduction
        let (idiv, scale) = calculateDIVI(base: .PLLD, desired: freq)                                //Using the faster (with known freq) available clock to reduce jitter

        // Configure the clock and divisor that will be used to generate the signal
        clockBasePointer.advanced(by: 41).pointee = CLKM_PASSWD | (idiv << CLKM_DIV_DIVI)            //CM CTL DIV register: Set DIVI value 
        clockBasePointer.advanced(by: 40).pointee = CLKM_PASSWD | CLKM_CTL_ENAB | CLKM_CTL_SRC_PLLD  //CM CTL register: Enable clock, MASH 0, source PLLD
        pwmBasePointer.pointee = 0                                                                   //PWM CTL register: Everything at 0, enable flag included, disables previous PWM
        usleep(10)
        // Configure the parameters for the M/S algorithm, S the number of total slots in RNG1 and M the number of slots with high value in DAT1
        let RNG = (channel == 0) ? 4 : 8
        let DAT = (channel == 0) ? 5 : 9
        pwmBasePointer.advanced(by: RNG).pointee = 100 * scale / highFreqSampleReduction                                    //RNG1 register
        pwmBasePointer.advanced(by: DAT).pointee = UInt((Float(percent) / Float(highFreqSampleReduction)) * Float(scale))   //DAT1 register
        let PWMCTL_MSEN = (channel == 0) ? PWMCTL_MSEN1 : PWMCTL_MSEN2
        let PWMCTL_PWEN = (channel == 0) ? PWMCTL_PWEN1 : PWMCTL_PWEN2
        pwmBasePointer.pointee = PWMCTL_MSEN | PWMCTL_PWEN                                                              //PWM CTL register, channel enabled, M/S mode
    }

    public func stopPWM() {
        pwmBasePointer.pointee = 0      //PWM CTL register, everything at 0, enable flag included
    }

    /// Maps a block of memory and returns the pointer
    internal func memmap(from mem_fd: Int32, at offset: Int) -> UnsafeMutablePointer<UInt> {
        let m = mmap(
            nil,                 //Any adddress in our space will do
            PAGE_SIZE,          //Map length
            PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
            MAP_SHARED,          //Shared with other processes
            mem_fd,              //File to map
            off_t(offset)     //Offset to GPIO peripheral
            )!

        if (Int(bitPattern: m) == -1) {    //MAP_FAILED not available, but its value is (void*)-1
            perror("mmap error")
            abort()
        }
        let pointer = m.assumingMemoryBound(to: UInt.self)

        return pointer
    }<commit_msg>Allow floating point duty in PWM. (#51)<commit_after>/*
   SwiftyGPIO

   Copyright (c) 2016 <NAME>
   Licensed under the MIT license, as follows:

   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.)
*/
#if os(Linux)
    import Glibc
#else
    import Darwin.C
#endif
import Foundation

extension SwiftyGPIO {

    public static func hardwarePWMs(for board: SupportedBoard) -> [Int:[GPIOName:PWMOutput]]? {
        switch board {
        case .RaspberryPiRev1:
            fallthrough
        case .RaspberryPiRev2:
            fallthrough
        case .RaspberryPiPlusZero:
            return PWMRPI1
        case .RaspberryPi2:
            fallthrough
        case .RaspberryPi3:
            return PWMRPI23
        default:
            return nil
        }
    }
}

// MARK: - SPI Presets
extension SwiftyGPIO {

    // RaspberryPis ARMv6 (all 1, Zero, Zero W) PWMs, only accessible ones, divided in channels (can use only one for each channel)
    static let PWMRPI1: [Int:[GPIOName:PWMOutput]] = [
        0: [.P12: RaspberryPWM(gpioId: 12, alt: 0, channel:0, baseAddr: 0x20000000), .P18: RaspberryPWM(gpioId: 18, alt: 5, channel:0, baseAddr: 0x20000000)],
        1: [.P13: RaspberryPWM(gpioId: 13, alt: 0, channel:1, baseAddr: 0x20000000), .P19: RaspberryPWM(gpioId: 19, alt: 5, channel:1, baseAddr: 0x20000000)]
    ]

    // RaspberryPis ARMv7 (2-3) PWMs, only accessible ones, divided in channels (can use only one for each channel)
    static let PWMRPI23: [Int:[GPIOName:PWMOutput]] = [
        0: [.P12: RaspberryPWM(gpioId: 12, alt: 0, channel:0, baseAddr: 0x3F000000), .P18: RaspberryPWM(gpioId: 18, alt: 5, channel:0, baseAddr: 0x3F000000)],
        1: [.P13: RaspberryPWM(gpioId: 13, alt: 0, channel:1, baseAddr: 0x3F000000), .P19: RaspberryPWM(gpioId: 19, alt: 5, channel:1, baseAddr: 0x3F000000)]
    ]
}

// MARK: PWM

public protocol PWMOutput {
    func initPWM()
    func startPWM(period ns: Int, duty percent: Float)
    func stopPWM()

    func initPWMPattern(bytes count: Int, at frequency: Int, with resetDelay: Int, dutyzero: Int, dutyone: Int)
    func sendDataWithPattern(values: [UInt8]) 
    func waitOnSendData()
    func cleanupPattern() 
}

public class RaspberryPWM: PWMOutput {
    let gpioId: UInt
    let alt: UInt
    let channel: Int

    let BCM2708_PERI_BASE: Int
    let GPIO_BASE: Int  // GPIO Register
    let PWM_BASE: Int   // PWM Register
    let CLOCK_BASE: Int // Clock Manager Register
    let BCM2708_PHY_BASE: Int = 0x7e000000
    let PWM_PHY_BASE: Int
    let PWMDMA = 5

    var gpioBasePointer: UnsafeMutablePointer<UInt>!
    var pwmBasePointer: UnsafeMutablePointer<UInt>!
    var clockBasePointer: UnsafeMutablePointer<UInt>!
    var dmaBasePointer: UnsafeMutablePointer<UInt>!
    var dmaCallbackPointer: UnsafeMutablePointer<DMACallback>! = nil
    var pwmRawPointer: UnsafeMutablePointer<UInt>! = nil
    var mailbox: MailBox! = nil

    var zeroPattern: Int = 0
    var onePattern: Int = 0
    var symbolBits: Int = 0
    var patternFrequency: Int = 0
    var patternDelay: Int = 0
    var dataLength: Int = 0

    public init(gpioId: UInt, alt: UInt, channel: Int, baseAddr: Int, dmanum: Int = 5) { //PWMDMA
        self.gpioId = gpioId
        self.alt = alt
        self.channel = channel
        BCM2708_PERI_BASE = baseAddr
        GPIO_BASE = BCM2708_PERI_BASE + 0x200000  // GPIO Register
        PWM_BASE =  BCM2708_PERI_BASE + 0x20C000  // PWM Register
        CLOCK_BASE = BCM2708_PERI_BASE + 0x101000 // Clock Manager Register
        PWM_PHY_BASE = BCM2708_PHY_BASE + 0x20C000 // PWM controller physical address
    }

    /// Init PWM on this pin, set alternative function
    public func initPWM() {
        var mem_fd: Int32 = 0

        //The only mem device that support PWM is /dev/mem
        mem_fd=open("/dev/mem", O_RDWR | O_SYNC)

        guard mem_fd > 0 else {
            fatalError("Can't open /dev/mem , use sudo!")
        }

        gpioBasePointer = memmap(from: mem_fd, at: GPIO_BASE)
        pwmBasePointer = memmap(from: mem_fd, at: PWM_BASE)
        clockBasePointer = memmap(from: mem_fd, at: CLOCK_BASE)

        let DMAOffsets: [Int] = [0x00007000, 0x00007100, 0x00007200, 0x00007300,
                         0x00007400, 0x00007500, 0x00007600, 0x00007700,
                         0x00007800, 0x00007900, 0x00007a00, 0x00007b00,
                         0x00007c00, 0x00007d00, 0x00007e00, 0x00e05000]

        func dmanumToPhysicalAddress(_ dmanum: Int) -> Int {
            guard dmanum < DMAOffsets.count else { return 0 }
            return BCM2708_PERI_BASE + DMAOffsets[dmanum]
        }

        var dma_addr = dmanumToPhysicalAddress(PWMDMA) // Address of a specific DMA Channel registers set
        let pageOffset = dma_addr % PAGE_SIZE
        dma_addr -= pageOffset

        let dma_map = UnsafeMutableRawPointer(memmap(from: mem_fd, at: dma_addr))
        dmaBasePointer = (dma_map + pageOffset).assumingMemoryBound(to: UInt.self)

        close(mem_fd)

        // set PWM alternate function for this GPIO
        setAlt()
    }

    /// Start a PWM signal with specific period in ns and duty cycle from 0 to 100.
    /// The signal starts, asynchronously(manged by a device external to the CPU), once this method is called and 
    /// needs to be stopped manually calling `stopPWM()`.
    public func startPWM(period ns: Int, duty percent: Float) {
        // Kill the clock
        clockBasePointer.advanced(by: 40).pointee = CLKM_PASSWD | CLKM_CTL_KILL     //CM CTL register: Set KILL flag
        usleep(10)

        // If the required frequency is too high, this value reduces the number of samples (scale does the opposite)
        let highFreqSampleReduction: UInt = (ns < 750) ? 10 : 1

        let freq: UInt = (1_000_000_000/UInt(ns)) * 100 / highFreqSampleReduction
        let (idiv, scale) = calculateDIVI(base: .PLLD, desired: freq)                                //Using the faster (with known freq) available clock to reduce jitter

        // Configure the clock and divisor that will be used to generate the signal
        clockBasePointer.advanced(by: 41).pointee = CLKM_PASSWD | (idiv << CLKM_DIV_DIVI)            //CM CTL DIV register: Set DIVI value 
        clockBasePointer.advanced(by: 40).pointee = CLKM_PASSWD | CLKM_CTL_ENAB | CLKM_CTL_SRC_PLLD  //CM CTL register: Enable clock, MASH 0, source PLLD
        pwmBasePointer.pointee = 0                                                                   //PWM CTL register: Everything at 0, enable flag included, disables previous PWM
        usleep(10)
        // Configure the parameters for the M/S algorithm, S the number of total slots in RNG1 and M the number of slots with high value in DAT1
        let RNG = (channel == 0) ? 4 : 8
        let DAT = (channel == 0) ? 5 : 9
        pwmBasePointer.advanced(by: RNG).pointee = 100 * scale / highFreqSampleReduction                                    //RNG1 register
        pwmBasePointer.advanced(by: DAT).pointee = UInt((percent / Float(highFreqSampleReduction)) * Float(scale))   //DAT1 register
        let PWMCTL_MSEN = (channel == 0) ? PWMCTL_MSEN1 : PWMCTL_MSEN2
        let PWMCTL_PWEN = (channel == 0) ? PWMCTL_PWEN1 : PWMCTL_PWEN2
        pwmBasePointer.pointee = PWMCTL_MSEN | PWMCTL_PWEN                                                              //PWM CTL register, channel enabled, M/S mode
    }

    public func stopPWM() {
        pwmBasePointer.pointee = 0      //PWM CTL register, everything at 0, enable flag included
    }

    /// Maps a block of memory and returns the pointer
    internal func memmap(from mem_fd: Int32, at offset: Int) -> UnsafeMutablePointer<UInt> {
        let m = mmap(
            nil,                 //Any adddress in our space will do
            PAGE_SIZE,          //Map length
            PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
            MAP_SHARED,          //Shared with other processes
            mem_fd,              //File to map
            off_t(offset)     //Offset to GPIO peripheral
            )!

        if (Int(bitPattern: m) == -1) {    //MAP_FAILED not available, but its value is (void*)-1
            perror("mmap error")
            abort()
        }
        let pointer = m.assumingMemoryBound(to: UInt.self)

        return pointer
    }
{'fraction_non_alphanumeric': 0.08046904416115325, 'fraction_numerical': 0.045075574202571134, 'mean_word_length': 2.5013182674199625, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1307338', 'n_tokens_mistral': 6454, 'n_tokens_neox': 5635, 'n_words': 1912}