diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e35bf08 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Vim stuff +*.swp diff --git a/README.md b/README.md index beb7211..80a4e54 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,18 @@ A simple example to turn on the TV: ```go package main -import "github.com/chbmuc/cec" +import ( + "flag" + + "github.com/chbmuc/cec" +) func main() { + flag.Parse() cec.Open("", "cec.go") - cec.PowerOn(0) + cec.PowerOn(cec.TV) } ``` + +To see log output from this package, add the '--logtostderr' flag when executing your binary. +To see debug output from libCEC, add the '--logtostderr' and '--v=2' flags when executing your binary. diff --git a/callbacks.go b/callbacks.go index ccfbc95..0345150 100644 --- a/callbacks.go +++ b/callbacks.go @@ -4,13 +4,13 @@ package cec import "C" import ( - "log" - "unsafe" + "unsafe" + + "github.com/golang/glog" ) //export logMessageCallback func logMessageCallback(c unsafe.Pointer, msg C.cec_log_message) C.int { - log.Println(C.GoString(&msg.message[0])) - - return 0; + glog.V(2).Info(C.GoString(&msg.message[0])) + return 0 } diff --git a/cec.go b/cec.go index 7f3156d..17bb5a6 100644 --- a/cec.go +++ b/cec.go @@ -1,76 +1,97 @@ package cec -import( - "log" +import ( "encoding/hex" - "time" "strings" + "time" + + "github.com/golang/glog" +) + +// Logical address values +const ( + TV int = iota + Recording + Recording2 + Tuner + Playback + Audio + Tuner2 + Tuner3 + Playback2 + Recording3 + Tuner4 + Playback3 + Reserved + Reserved2 + Free + Broadcast ) type Device struct { - OSDName string - Vendor string - LogicalAddress int - ActiveSource bool - PowerStatus string + OSDName string + Vendor string + LogicalAddress int + ActiveSource bool + PowerStatus string PhysicalAddress string } -var logicalNames = []string{ "TV", "Recording", "Recording2", "Tuner", - "Playback","Audio", "Tuner2", "Tuner3", +var logicalNames = []string{"TV", "Recording", "Recording2", "Tuner", + "Playback", "Audio", "Tuner2", "Tuner3", "Playback2", "Recording3", "Tuner4", "Playback3", - "Reserved", "Reserved2", "Free", "Broadcast" } - -var vendorList = map[uint64]string{ 0x000039:"Toshiba", 0x0000F0:"Samsung", - 0x0005CD:"Denon", 0x000678:"Marantz", 0x000982:"Loewe", 0x0009B0:"Onkyo", - 0x000CB8:"Medion", 0x000CE7:"Toshiba", 0x001582:"Pulse Eight", - 0x0020C7:"Akai", 0x002467:"Aoc", 0x008045:"Panasonic", 0x00903E:"Philips", - 0x009053:"Daewoo", 0x00A0DE:"Yamaha", 0x00D0D5:"Grundig", - 0x00E036:"Pioneer", 0x00E091:"LG", 0x08001F:"Sharp", 0x080046:"Sony", - 0x18C086:"Broadcom", 0x6B746D:"Vizio", 0x8065E9:"Benq", - 0x9C645E:"Harman Kardon" } - -var keyList = map[int]string{ 0x00:"Select", 0x01:"Up", 0x02:"Down", 0x03:"Left", - 0x04:"Right", 0x05:"RightUp", 0x06:"RightDown", 0x07:"LeftUp", - 0x08:"LeftDown", 0x09:"RootMenu", 0x0A:"SetupMenu", 0x0B:"ContentsMenu", - 0x0C:"FavoriteMenu", 0x0D:"Exit", 0x20:"0", 0x21:"1", 0x22:"2", 0x23:"3", - 0x24:"4", 0x25:"5", 0x26:"6", 0x27:"7", 0x28:"8", 0x29:"9", 0x2A:"Dot", - 0x2B:"Enter", 0x2C:"Clear", 0x2F:"NextFavorite", 0x30:"ChannelUp", - 0x31:"ChannelDown", 0x32:"PreviousChannel", 0x33:"SoundSelect", - 0x34:"InputSelect", 0x35:"DisplayInformation", 0x36:"Help", - 0x37:"PageUp", 0x38:"PageDown", 0x40:"Power", 0x41:"VolumeUp", - 0x42:"VolumeDown", 0x43:"Mute", 0x44:"Play", 0x45:"Stop", 0x46:"Pause", - 0x47:"Record", 0x48:"Rewind", 0x49:"FastForward", 0x4A:"Eject", - 0x4B:"Forward", 0x4C:"Backward", 0x4D:"StopRecord", 0x4E:"PauseRecord", - 0x50:"Angle", 0x51:"SubPicture", 0x52:"VideoOnDemand", - 0x53:"ElectronicProgramGuide", 0x54:"TimerProgramming", - 0x55:"InitialConfiguration", 0x60:"PlayFunction", 0x61:"PausePlay", - 0x62:"RecordFunction", 0x63:"PauseRecordFunction", - 0x64:"StopFunction", 0x65:"Mute", - 0x66:"RestoreVolume", 0x67:"Tune", 0x68:"SelectMedia", - 0x69:"SelectAvInput", 0x6A:"SelectAudioInput", 0x6B:"PowerToggle", - 0x6C:"PowerOff", 0x6D:"PowerOn", 0x71:"Blue", 0X72:"Red", 0x73:"Green", - 0x74:"Yellow", 0x75:"F5", 0x76:"Data", 0x91:"AnReturn", - 0x96:"Max" } + "Reserved", "Reserved2", "Free", "Broadcast"} + +var vendorList = map[uint64]string{0x000039: "Toshiba", 0x0000F0: "Samsung", + 0x0005CD: "Denon", 0x000678: "Marantz", 0x000982: "Loewe", 0x0009B0: "Onkyo", + 0x000CB8: "Medion", 0x000CE7: "Toshiba", 0x001582: "Pulse Eight", + 0x0020C7: "Akai", 0x002467: "Aoc", 0x008045: "Panasonic", 0x00903E: "Philips", + 0x009053: "Daewoo", 0x00A0DE: "Yamaha", 0x00D0D5: "Grundig", + 0x00E036: "Pioneer", 0x00E091: "LG", 0x08001F: "Sharp", 0x080046: "Sony", + 0x18C086: "Broadcom", 0x6B746D: "Vizio", 0x8065E9: "Benq", + 0x9C645E: "Harman Kardon"} + +var keyList = map[int]string{0x00: "Select", 0x01: "Up", 0x02: "Down", 0x03: "Left", + 0x04: "Right", 0x05: "RightUp", 0x06: "RightDown", 0x07: "LeftUp", + 0x08: "LeftDown", 0x09: "RootMenu", 0x0A: "SetupMenu", 0x0B: "ContentsMenu", + 0x0C: "FavoriteMenu", 0x0D: "Exit", 0x20: "0", 0x21: "1", 0x22: "2", 0x23: "3", + 0x24: "4", 0x25: "5", 0x26: "6", 0x27: "7", 0x28: "8", 0x29: "9", 0x2A: "Dot", + 0x2B: "Enter", 0x2C: "Clear", 0x2F: "NextFavorite", 0x30: "ChannelUp", + 0x31: "ChannelDown", 0x32: "PreviousChannel", 0x33: "SoundSelect", + 0x34: "InputSelect", 0x35: "DisplayInformation", 0x36: "Help", + 0x37: "PageUp", 0x38: "PageDown", 0x40: "Power", 0x41: "VolumeUp", + 0x42: "VolumeDown", 0x43: "Mute", 0x44: "Play", 0x45: "Stop", 0x46: "Pause", + 0x47: "Record", 0x48: "Rewind", 0x49: "FastForward", 0x4A: "Eject", + 0x4B: "Forward", 0x4C: "Backward", 0x4D: "StopRecord", 0x4E: "PauseRecord", + 0x50: "Angle", 0x51: "SubPicture", 0x52: "VideoOnDemand", + 0x53: "ElectronicProgramGuide", 0x54: "TimerProgramming", + 0x55: "InitialConfiguration", 0x60: "PlayFunction", 0x61: "PausePlay", + 0x62: "RecordFunction", 0x63: "PauseRecordFunction", + 0x64: "StopFunction", 0x65: "Mute", + 0x66: "RestoreVolume", 0x67: "Tune", 0x68: "SelectMedia", + 0x69: "SelectAvInput", 0x6A: "SelectAudioInput", 0x6B: "PowerToggle", + 0x6C: "PowerOff", 0x6D: "PowerOn", 0x71: "Blue", 0X72: "Red", 0x73: "Green", + 0x74: "Yellow", 0x75: "F5", 0x76: "Data", 0x91: "AnReturn", + 0x96: "Max"} func Open(name string, deviceName string) { var config CECConfiguration config.DeviceName = deviceName if er := cecInit(config); er != nil { - log.Println(er) - return + glog.Error(er) + return } adapter, er := getAdapter(name) if er != nil { - log.Println(er) + glog.Error(er) return } er = openAdapter(adapter) if er != nil { - log.Println(er) + glog.Error(er) return } } @@ -82,8 +103,8 @@ func Key(address int, key interface{}) { case string: if key[:2] == "0x" && len(key) == 4 { keybytes, err := hex.DecodeString(key[2:]) - if err != nil { - log.Println(err) + if err != nil { + glog.Error(err) return } keycode = int(keybytes[0]) @@ -93,18 +114,18 @@ func Key(address int, key interface{}) { case int: keycode = key default: - log.Println("Invalid key type") + glog.Error("Invalid key type") return } er := KeyPress(address, keycode) if er != nil { - log.Println(er) + glog.Error(er) return } time.Sleep(10 * time.Millisecond) er = KeyRelease(address) if er != nil { - log.Println(er) + glog.Error(er) return } } @@ -115,7 +136,7 @@ func List() map[string]Device { active_devices := GetActiveDevices() for address, active := range active_devices { - if (active) { + if active { var dev Device dev.LogicalAddress = address @@ -132,15 +153,15 @@ func List() map[string]Device { } func removeSeparators(in string) string { - // remove separators (":", "-", " ", "_") - out := strings.Map(func(r rune) rune { - if strings.IndexRune(":-_ ", r) < 0 { - return r - } - return -1 - }, in) - - return(out) + // remove separators (":", "-", " ", "_") + out := strings.Map(func(r rune) rune { + if strings.IndexRune(":-_ ", r) < 0 { + return r + } + return -1 + }, in) + + return (out) } func GetKeyCodeByName(name string) int { @@ -166,7 +187,7 @@ func GetLogicalAddressByName(name string) int { name = strings.ToLower(name) - for i:=0; i<16; i++ { + for i := 0; i < 16; i++ { if strings.ToLower(logicalNames[i]) == name { return i } diff --git a/libcec.go b/libcec.go index e91711e..822a682 100644 --- a/libcec.go +++ b/libcec.go @@ -1,7 +1,7 @@ package cec -/* -#cgo pkg-config: libcec +/* +#cgo pkg-config: libcec #include #include @@ -37,167 +37,177 @@ void setLogicalAddress(cec_logical_addresses* addresses, cec_logical_address add addresses->addresses[(int) address] = 1; } -*/ +*/ import "C" import ( - "errors" "encoding/hex" - "strings" - "log" + "errors" "fmt" + "strings" + "unsafe" + + "github.com/golang/glog" ) -type CECConfiguration struct { - DeviceName string -} +type CECConfiguration struct { + DeviceName string +} - -type CECAdapter struct { - Path string - Comm string -} +type CECAdapter struct { + Path string + Comm string +} + +const ( + // Versions hardcoded to 3.1.0 + clientVersion = C.uint32_t(0x00030100) + serverVersion = C.uint32_t(0x00030100) +) -func cecInit(config CECConfiguration) error { - var conf C.libcec_configuration +var conn C.libcec_connection_t - conf.clientVersion = C.uint32_t(C.CEC_CLIENT_VERSION_CURRENT) - conf.serverVersion = C.uint32_t(C.CEC_SERVER_VERSION_CURRENT) +func cecInit(config CECConfiguration) error { + var conf C.libcec_configuration - for i:=0; i<5; i++ { + conf.clientVersion = clientVersion + conf.serverVersion = serverVersion + + for i := 0; i < 5; i++ { conf.deviceTypes.types[i] = C.CEC_DEVICE_TYPE_RESERVED - } + } conf.deviceTypes.types[0] = C.CEC_DEVICE_TYPE_RECORDING_DEVICE C.setName(&conf, C.CString(config.DeviceName)) - C.setupCallbacks(&conf) + C.setupCallbacks(&conf) - result := C.cec_initialise(&conf) - if result < 1 { - return errors.New("Failed to init CEC") + conn = C.libcec_initialise(&conf) + if uintptr(conn) < 1 { + return errors.New("Failed to init CEC") } - return nil -} + return nil +} -func getAdapter(name string) (CECAdapter, error) { - var adapter CECAdapter +func getAdapter(name string) (CECAdapter, error) { + var adapter CECAdapter - var deviceList [10]C.cec_adapter - devicesFound := int(C.cec_find_adapters(&deviceList[0], 10, nil)) + var deviceList [10]C.cec_adapter + devicesFound := int(C.libcec_find_adapters(conn, &deviceList[0], 10, nil)) - for i:=0; i < devicesFound; i++ { - device := deviceList[i] - adapter.Path = C.GoStringN(&device.path[0], 1024) - adapter.Comm = C.GoStringN(&device.comm[0], 1024) + for i := 0; i < devicesFound; i++ { + device := deviceList[i] + adapter.Path = C.GoStringN(&device.path[0], 1024) + adapter.Comm = C.GoStringN(&device.comm[0], 1024) if strings.Contains(adapter.Path, name) || strings.Contains(adapter.Comm, name) { - return adapter, nil + return adapter, nil } } - return adapter, errors.New("No Device Found") + return adapter, errors.New("No Device Found") } -func openAdapter(adapter CECAdapter) error { - C.cec_init_video_standalone() +func openAdapter(adapter CECAdapter) error { + C.libcec_init_video_standalone(conn) - result := C.cec_open(C.CString(adapter.Comm), C.CEC_DEFAULT_CONNECT_TIMEOUT) - if result < 1 { - return errors.New("Failed to open adapter") - } + result := C.libcec_open(conn, C.CString(adapter.Comm), C.CEC_DEFAULT_CONNECT_TIMEOUT) + if result < 1 { + return errors.New("Failed to open adapter") + } - return nil -} + return nil +} func Transmit(command string) { - var cec_command C.cec_command - - cmd, err := hex.DecodeString(removeSeparators(command)) - if err != nil { - log.Fatal(err) - } - cmd_len := len(cmd) - - if (cmd_len > 0) { - cec_command.initiator = C.cec_logical_address((cmd[0] >> 4) & 0xF) - cec_command.destination = C.cec_logical_address(cmd[0] & 0xF) - if (cmd_len > 1) { - cec_command.opcode_set = 1 - cec_command.opcode = C.cec_opcode(cmd[1]) - } else { - cec_command.opcode_set = 0 - } - if (cmd_len > 2) { - cec_command.parameters.size = C.uint8_t(cmd_len-2) - for i := 0; i < cmd_len-2; i++ { - cec_command.parameters.data[i] = C.uint8_t(cmd[i+2]) - } - } else { - cec_command.parameters.size = 0 - } - } - - C.cec_transmit((*C.cec_command)(&cec_command)) + var cec_command C.cec_command + + cmd, err := hex.DecodeString(removeSeparators(command)) + if err != nil { + glog.Fatal(err) + } + cmd_len := len(cmd) + + if cmd_len > 0 { + cec_command.initiator = C.cec_logical_address((cmd[0] >> 4) & 0xF) + cec_command.destination = C.cec_logical_address(cmd[0] & 0xF) + if cmd_len > 1 { + cec_command.opcode_set = 1 + cec_command.opcode = C.cec_opcode(cmd[1]) + } else { + cec_command.opcode_set = 0 + } + if cmd_len > 2 { + cec_command.parameters.size = C.uint8_t(cmd_len - 2) + for i := 0; i < cmd_len-2; i++ { + cec_command.parameters.data[i] = C.uint8_t(cmd[i+2]) + } + } else { + cec_command.parameters.size = 0 + } + } + + C.libcec_transmit(conn, (*C.cec_command)(&cec_command)) } func Destroy() { - C.cec_destroy() + C.libcec_destroy(conn) + conn = C.libcec_connection_t(unsafe.Pointer(uintptr(0))) } func PowerOn(address int) error { - if C.cec_power_on_devices(C.cec_logical_address(address)) != 0 { + if C.libcec_power_on_devices(conn, C.cec_logical_address(address)) != 0 { return errors.New("Error in cec_power_on_devices") } return nil } func Standby(address int) error { - if C.cec_standby_devices(C.cec_logical_address(address)) != 0 { - return errors.New("Error in cec_standby_devices") + if C.libcec_standby_devices(conn, C.cec_logical_address(address)) != 0 { + return errors.New("Error in libcec_standby_devices") } return nil } func VolumeUp() error { - if C.cec_volume_up(1) != 0 { - return errors.New("Error in cec_volume_up") + if C.libcec_volume_up(conn, 1) != 0 { + return errors.New("Error in libcec_volume_up") } return nil } func VolumeDown() error { - if C.cec_volume_down(1) != 0 { - return errors.New("Error in cec_volume_down") + if C.libcec_volume_down(conn, 1) != 0 { + return errors.New("Error in libcec_volume_down") } return nil } func Mute() error { - if C.cec_mute_audio(1) != 0 { - return errors.New("Error in cec_mute_audio") + if C.libcec_mute_audio(conn, 1) != 0 { + return errors.New("Error in libcec_mute_audio") } return nil } func KeyPress(address int, key int) error { - if C.cec_send_keypress(C.cec_logical_address(address), C.cec_user_control_code(key), 1) != 1 { - return errors.New("Error in cec_send_keypress") + if C.libcec_send_keypress(conn, C.cec_logical_address(address), C.cec_user_control_code(key), 1) != 1 { + return errors.New("Error in libcec_send_keypress") } return nil } func KeyRelease(address int) error { - if C.cec_send_key_release(C.cec_logical_address(address), 1) != 1 { - return errors.New("Error in cec_send_key_release") + if C.libcec_send_key_release(conn, C.cec_logical_address(address), 1) != 1 { + return errors.New("Error in libcec_send_key_release") } return nil } func GetActiveDevices() [16]bool { var devices [16]bool - result := C.cec_get_active_devices() + result := C.libcec_get_active_devices(conn) - for i:=0; i < 16; i++ { + for i := 0; i < 16; i++ { if int(result.addresses[i]) > 0 { devices[i] = true } @@ -207,13 +217,13 @@ func GetActiveDevices() [16]bool { } func GetDeviceOSDName(address int) string { - result := C.cec_get_device_osd_name(C.cec_logical_address(address)) + result := C.libcec_get_device_osd_name(conn, C.cec_logical_address(address)) return C.GoString(&result.name[0]) } func IsActiveSource(address int) bool { - result := C.cec_is_active_source(C.cec_logical_address(address)) + result := C.libcec_is_active_source(conn, C.cec_logical_address(address)) if int(result) != 0 { return true @@ -223,19 +233,19 @@ func IsActiveSource(address int) bool { } func GetDeviceVendorId(address int) uint64 { - result := C.cec_get_device_vendor_id(C.cec_logical_address(address)) + result := C.libcec_get_device_vendor_id(conn, C.cec_logical_address(address)) return uint64(result) } func GetDevicePhysicalAddress(address int) string { - result := C.cec_get_device_physical_address(C.cec_logical_address(address)) + result := C.libcec_get_device_physical_address(conn, C.cec_logical_address(address)) - return fmt.Sprintf("%x.%x.%x.%x", (uint(result) >> 12) & 0xf, (uint(result) >> 8) & 0xf, (uint(result) >> 4) & 0xf, uint(result) & 0xf) + return fmt.Sprintf("%x.%x.%x.%x", (uint(result)>>12)&0xf, (uint(result)>>8)&0xf, (uint(result)>>4)&0xf, uint(result)&0xf) } func GetDevicePowerStatus(address int) string { - result := C.cec_get_device_power_status(C.cec_logical_address(address)) + result := C.libcec_get_device_power_status(conn, C.cec_logical_address(address)) // C.CEC_POWER_STATUS_UNKNOWN == error