1use linux_cec_sys::constants::{
9 CEC_CONNECTOR_TYPE_DRM, CEC_CONNECTOR_TYPE_NO_CONNECTOR, CEC_EVENT_LOST_MSGS,
10 CEC_EVENT_PIN_5V_HIGH, CEC_EVENT_PIN_5V_LOW, CEC_EVENT_PIN_CEC_HIGH, CEC_EVENT_PIN_CEC_LOW,
11 CEC_EVENT_PIN_HPD_HIGH, CEC_EVENT_PIN_HPD_LOW, CEC_EVENT_STATE_CHANGE, CEC_MAX_LOG_ADDRS,
12};
13use linux_cec_sys::ioctls::{
14 adapter_get_capabilities, adapter_get_connector_info, adapter_get_logical_addresses,
15 adapter_get_physical_address, adapter_set_logical_addresses, adapter_set_physical_address,
16 dequeue_event, get_mode, receive_message, set_mode, transmit_message,
17};
18use linux_cec_sys::structs::{
19 cec_caps, cec_connector_info, cec_drm_connector_info, cec_event, cec_log_addrs, cec_msg,
20 CEC_RX_STATUS, CEC_TX_STATUS,
21};
22use linux_cec_sys::{PhysicalAddress as SysPhysicalAddress, Timestamp, VendorId as SysVendorId};
23use nix::fcntl::{fcntl, FcntlArg, OFlag};
24use nix::poll::{poll, PollFd, PollFlags};
25use num_enum::TryFromPrimitive;
26use std::ffi::{c_char, OsStr, OsString};
27use std::fs::{File, OpenOptions};
28use std::os::fd::{AsFd, AsRawFd, OwnedFd};
29use std::os::unix::ffi::OsStrExt;
30use std::path::Path;
31use std::str::FromStr;
32use tinyvec::ArrayVec;
33#[cfg(feature = "tracing")]
34use tracing::{debug, warn};
35
36pub use linux_cec_sys::structs::CEC_CAP as Capabilities;
37pub use nix::poll::PollTimeout;
38
39use crate::ioctls::CecMessageHandlingMode;
40use crate::message::{Message, Opcode};
41use crate::operand::{BufferOperand, UiCommand};
42use crate::{
43 Error, FollowerMode, InitiatorMode, LogicalAddress, LogicalAddressType, PhysicalAddress, Range,
44 Result, RxError, Timeout, VendorId,
45};
46
47#[cfg(feature = "async")]
48pub use crate::async_support::{AsyncDevice, AsyncDevicePoller};
49
50#[derive(Debug)]
52pub struct Device {
53 file: File,
54 tx_logical_address: LogicalAddress,
55 internal_log_addrs: cec_log_addrs,
56}
57
58#[derive(Debug, Copy, Clone, PartialEq, Hash)]
60pub enum MessageData {
61 Valid(Message),
63 Invalid(ArrayVec<[u8; 14]>),
65}
66
67impl MessageData {
68 #[must_use]
69 pub fn opcode(&self) -> u8 {
70 match self {
71 MessageData::Valid(message) => message.opcode().into(),
72 MessageData::Invalid(bytes) => bytes[0],
73 }
74 }
75
76 #[must_use]
77 pub fn to_bytes(&self) -> Vec<u8> {
78 match self {
79 MessageData::Valid(message) => message.to_bytes(),
80 MessageData::Invalid(bytes) => bytes.to_vec(),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Hash)]
87pub struct Envelope {
88 pub message: MessageData,
90 pub initiator: LogicalAddress,
92 pub destination: LogicalAddress,
97 pub timestamp: Timestamp,
100 pub sequence: u32,
102}
103
104impl TryFrom<cec_msg> for Envelope {
105 type Error = Error;
106
107 fn try_from(message: cec_msg) -> Result<Envelope> {
108 if message.rx_status.contains(CEC_RX_STATUS::TIMEOUT) {
109 return Err(Error::Timeout);
110 }
111 if message.rx_status.contains(CEC_RX_STATUS::ABORTED) {
112 return Err(Error::Abort);
113 }
114 if message.rx_status.contains(CEC_RX_STATUS::FEATURE_ABORT) {
115 return Err(RxError::FeatureAbort.into());
116 }
117 if !(2..=15).contains(&message.len) {
118 return Err(Error::InvalidData);
119 }
120 let bytes = &message.msg[1..message.len as usize];
121 let initiator = LogicalAddress::try_from_primitive(message.msg[0] >> 4)?;
122 let destination = LogicalAddress::try_from_primitive(message.msg[0] & 0xF)?;
123 let timestamp = message.rx_ts;
124 let sequence = message.sequence;
125
126 let message = match Message::try_from_bytes(bytes) {
127 Ok(message) => MessageData::Valid(message),
128 Err(e) => {
129 #[cfg(feature = "tracing")]
130 warn!("Failed to parse incoming message {bytes:?}: {e}");
131 let _ = e;
132 MessageData::Invalid(ArrayVec::from_array_len(
133 message.msg[1..15].try_into().unwrap(),
134 bytes.len(),
135 ))
136 }
137 };
138
139 let envelope = Envelope {
140 message,
141 initiator,
142 destination,
143 timestamp,
144 sequence,
145 };
146 #[cfg(feature = "tracing")]
147 debug!("Got message {envelope:#?}");
148 Ok(envelope)
149 }
150}
151
152#[cfg(test)]
153mod test_envelope {
154 use super::*;
155 use crate::sys::CEC_MSG_FL;
156
157 #[test]
158 fn decode_simple() {
159 let msg = cec_msg {
160 tx_ts: 0,
161 rx_ts: 911462400,
162 len: 2,
163 timeout: 0,
164 sequence: 1,
165 flags: CEC_MSG_FL::empty(),
166 msg: [
167 0xF,
168 Opcode::Standby as u8,
169 0,
170 0,
171 0,
172 0,
173 0,
174 0,
175 0,
176 0,
177 0,
178 0,
179 0,
180 0,
181 0,
182 0,
183 ],
184 reply: 0,
185 rx_status: CEC_RX_STATUS::OK,
186 tx_status: CEC_TX_STATUS::empty(),
187 tx_arb_lost_cnt: 0,
188 tx_nack_cnt: 0,
189 tx_low_drive_cnt: 0,
190 tx_error_cnt: 0,
191 };
192
193 let envelope = Envelope::try_from(msg).unwrap();
194 assert_eq!(envelope.message.opcode(), Opcode::Standby as u8);
195 assert_eq!(envelope.initiator, LogicalAddress::Tv);
196 assert_eq!(envelope.destination, LogicalAddress::Broadcast);
197 assert_eq!(envelope.timestamp, 911462400);
198 assert_eq!(envelope.sequence, 1);
199 let MessageData::Valid(message) = envelope.message else {
200 panic!();
201 };
202 assert_eq!(message.opcode(), Opcode::Standby);
203 }
204
205 #[test]
206 fn decode_invalid_opcode() {
207 let msg = cec_msg {
208 tx_ts: 0,
209 rx_ts: 0,
210 len: 2,
211 timeout: 0,
212 sequence: 1,
213 flags: CEC_MSG_FL::empty(),
214 msg: [0xF, 0xFE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
215 reply: 0,
216 rx_status: CEC_RX_STATUS::OK,
217 tx_status: CEC_TX_STATUS::empty(),
218 tx_arb_lost_cnt: 0,
219 tx_nack_cnt: 0,
220 tx_low_drive_cnt: 0,
221 tx_error_cnt: 0,
222 };
223
224 let envelope = Envelope::try_from(msg).unwrap();
225 assert_eq!(envelope.message.opcode(), 0xFE);
226 let MessageData::Invalid(message) = envelope.message else {
227 panic!();
228 };
229 assert_eq!(message.as_slice(), &[0xFE]);
230 }
231
232 #[test]
233 fn decode_too_long() {
234 let msg = cec_msg {
235 tx_ts: 0,
236 rx_ts: 0,
237 len: 16,
238 timeout: 0,
239 sequence: 1,
240 flags: CEC_MSG_FL::empty(),
241 msg: [
242 0xF,
243 Opcode::Standby as u8,
244 0,
245 0,
246 0,
247 0,
248 0,
249 0,
250 0,
251 0,
252 0,
253 0,
254 0,
255 0,
256 0,
257 0,
258 ],
259 reply: 0,
260 rx_status: CEC_RX_STATUS::OK,
261 tx_status: CEC_TX_STATUS::empty(),
262 tx_arb_lost_cnt: 0,
263 tx_nack_cnt: 0,
264 tx_low_drive_cnt: 0,
265 tx_error_cnt: 0,
266 };
267
268 let Err(err) = Envelope::try_from(msg) else {
269 panic!();
270 };
271 assert_eq!(err, Error::InvalidData);
272 }
273
274 #[test]
275 fn decode_way_too_short() {
276 let msg = cec_msg {
277 tx_ts: 0,
278 rx_ts: 0,
279 len: 0,
280 timeout: 0,
281 sequence: 1,
282 flags: CEC_MSG_FL::empty(),
283 msg: [0; 16],
284 reply: 0,
285 rx_status: CEC_RX_STATUS::OK,
286 tx_status: CEC_TX_STATUS::empty(),
287 tx_arb_lost_cnt: 0,
288 tx_nack_cnt: 0,
289 tx_low_drive_cnt: 0,
290 tx_error_cnt: 0,
291 };
292
293 let Err(err) = Envelope::try_from(msg) else {
294 panic!();
295 };
296 assert_eq!(err, Error::InvalidData);
297 }
298
299 #[test]
300 fn decode_too_short() {
301 let msg = cec_msg {
302 tx_ts: 0,
303 rx_ts: 0,
304 len: 1,
305 timeout: 0,
306 sequence: 1,
307 flags: CEC_MSG_FL::empty(),
308 msg: [0; 16],
309 reply: 0,
310 rx_status: CEC_RX_STATUS::OK,
311 tx_status: CEC_TX_STATUS::empty(),
312 tx_arb_lost_cnt: 0,
313 tx_nack_cnt: 0,
314 tx_low_drive_cnt: 0,
315 tx_error_cnt: 0,
316 };
317
318 let Err(err) = Envelope::try_from(msg) else {
319 panic!();
320 };
321 assert_eq!(err, Error::InvalidData);
322 }
323}
324
325#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
327#[non_exhaustive]
328pub enum Pin {
329 Cec,
331 HotPlugDetect,
333 Power5V,
335}
336
337#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
339pub enum PinState {
340 Low,
341 High,
342}
343
344#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
350pub struct PinEvent {
351 pub pin: Pin,
353 pub state: PinState,
355}
356
357#[derive(Debug)]
360pub struct DevicePoller {
361 fd: OwnedFd,
362}
363
364#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
370#[non_exhaustive]
371pub enum PollStatus {
372 Nothing,
373 Destroyed,
374 GotEvent,
375 GotMessage,
376 GotAll,
377}
378
379#[derive(Debug, Clone, Hash)]
382#[non_exhaustive]
383pub enum PollResult {
384 Message(Envelope),
386 PinEvent(PinEvent),
388 LostMessages(u32),
392 StateChange,
396}
397
398#[derive(Debug, Clone, PartialEq, Hash)]
400#[non_exhaustive]
401pub enum ConnectorInfo {
402 None,
403 DrmConnector {
405 card_no: u32,
407 connector_id: u32,
409 },
410 Unknown {
411 ty: u32,
412 data: [u32; 16],
413 },
414}
415
416impl Device {
417 pub fn open(path: impl AsRef<Path>) -> Result<Device> {
420 let file = OpenOptions::new()
421 .read(true)
422 .write(true)
423 .create(false)
424 .open(path)?;
425 Device::try_from(file)
426 }
427
428 pub fn get_poller(&self) -> Result<DevicePoller> {
436 Ok(DevicePoller {
437 fd: self.file.as_fd().try_clone_to_owned()?,
438 })
439 }
440
441 pub fn poll(&mut self, timeout: PollTimeout) -> Result<Vec<PollResult>> {
445 let poller = self.get_poller()?;
446 let status = poller.poll(timeout)?;
447 self.handle_status(status)
448 }
449
450 pub fn set_blocking(&self, blocking: bool) -> Result<()> {
452 let rawfd = self.file.as_fd();
453 let mut flags = OFlag::from_bits_retain(fcntl(rawfd, FcntlArg::F_GETFL)?);
454 flags.set(OFlag::O_NONBLOCK, !blocking);
455 fcntl(rawfd, FcntlArg::F_SETFL(flags))?;
456 Ok(())
457 }
458
459 pub fn get_initiator_mode(&self) -> Result<InitiatorMode> {
461 self.get_mode()?.initiator().try_into()
462 }
463
464 pub fn set_initiator_mode(&self, mode: InitiatorMode) -> Result<()> {
466 let mode = self.get_mode()?.with_initiator(mode.into());
467 self.set_mode(mode)
468 }
469
470 pub fn get_follower_mode(&self) -> Result<FollowerMode> {
472 self.get_mode()?.follower().try_into()
473 }
474
475 pub fn set_follower_mode(&self, mode: FollowerMode) -> Result<()> {
477 let mode = self.get_mode()?.with_follower(mode.into());
478 self.set_mode(mode)
479 }
480
481 pub fn get_raw_capabilities(&self) -> Result<cec_caps> {
483 let mut caps = cec_caps::default();
484 unsafe {
485 adapter_get_capabilities(self.file.as_raw_fd(), &mut caps)?;
486 }
487 Ok(caps)
488 }
489
490 pub fn get_capabilities(&self) -> Result<Capabilities> {
493 self.get_raw_capabilities().map(|caps| caps.capabilities)
494 }
495
496 pub fn get_driver_name(&self) -> Result<OsString> {
498 self.get_raw_capabilities().map(|caps| {
499 let driver =
500 unsafe { &*std::ptr::from_ref::<[c_char; 32]>(&caps.driver).cast::<[u8; 32]>() };
501 OsStr::from_bytes(driver).to_os_string()
502 })
503 }
504
505 pub fn get_adapter_name(&self) -> Result<OsString> {
507 self.get_raw_capabilities().map(|caps| {
508 let adapter =
509 unsafe { &*std::ptr::from_ref::<[c_char; 32]>(&caps.name).cast::<[u8; 32]>() };
510 OsStr::from_bytes(adapter).to_os_string()
511 })
512 }
513
514 pub fn get_physical_address(&self) -> Result<PhysicalAddress> {
516 let mut phys_addr: SysPhysicalAddress = 0;
517 unsafe {
518 adapter_get_physical_address(self.file.as_raw_fd(), &mut phys_addr)?;
519 }
520 Ok(PhysicalAddress(phys_addr))
521 }
522
523 pub fn set_physical_address(&self, phys_addr: PhysicalAddress) -> Result<()> {
526 unsafe {
527 adapter_set_physical_address(self.file.as_raw_fd(), &phys_addr.0)?;
528 }
529 Ok(())
530 }
531
532 pub fn get_logical_addresses(&mut self) -> Result<Vec<LogicalAddress>> {
534 unsafe {
535 adapter_get_logical_addresses(self.file.as_raw_fd(), &mut self.internal_log_addrs)?;
536 }
537
538 let mut vec = Vec::new();
539 for index in 0..self.internal_log_addrs.num_log_addrs {
540 vec.push(self.internal_log_addrs.log_addr[index as usize].try_into()?);
541 }
542 Ok(vec)
543 }
544
545 pub fn set_logical_addresses(&mut self, log_addrs: &[LogicalAddressType]) -> Result<()> {
549 Range::AtMost(CEC_MAX_LOG_ADDRS).check(log_addrs.len(), "logical addresses")?;
550
551 #[cfg(feature = "tracing")]
552 debug!("Attempting to set logical addresses: {log_addrs:?}");
553
554 for (index, log_addr) in log_addrs.iter().enumerate() {
555 self.internal_log_addrs.log_addr_type[index] = (*log_addr).into();
556 if let Some(prim_dev_type) = (*log_addr).primary_device_type() {
557 self.internal_log_addrs.primary_device_type[index] = prim_dev_type.into();
558 } else {
559 self.internal_log_addrs.primary_device_type[index] = 0xFF;
560 }
561 }
562
563 if !log_addrs.is_empty() && self.internal_log_addrs.num_log_addrs > 0 {
564 self.clear_logical_addresses()?;
566 }
567
568 self.internal_log_addrs.num_log_addrs = log_addrs.len().try_into().unwrap();
569 unsafe {
570 adapter_set_logical_addresses(self.file.as_raw_fd(), &mut self.internal_log_addrs)?;
571 }
572 if !log_addrs.is_empty() {
573 self.tx_logical_address =
574 LogicalAddress::try_from_primitive(self.internal_log_addrs.log_addr[0])
575 .unwrap_or(LogicalAddress::Unregistered);
576 }
577 Ok(())
578 }
579
580 pub fn set_logical_address(&mut self, log_addr: LogicalAddressType) -> Result<()> {
584 self.set_logical_addresses(&[log_addr])
585 }
586
587 pub fn clear_logical_addresses(&mut self) -> Result<()> {
590 self.internal_log_addrs.num_log_addrs = 0;
591 self.tx_logical_address = LogicalAddress::Unregistered;
592 unsafe {
593 adapter_set_logical_addresses(self.file.as_raw_fd(), &mut self.internal_log_addrs)?;
594 }
595 Ok(())
596 }
597
598 pub fn get_osd_name(&mut self) -> Result<OsString> {
600 unsafe {
601 adapter_get_logical_addresses(self.file.as_raw_fd(), &mut self.internal_log_addrs)?;
602 }
603 Ok(OsStr::from_bytes(&self.internal_log_addrs.osd_name).to_os_string())
604 }
605
606 pub fn set_osd_name(&mut self, name: &str) -> Result<()> {
620 let name_buffer = BufferOperand::from_str(name)?;
621 #[cfg(feature = "tracing")]
622 debug!("Setting OSD name to {name}");
623 self.internal_log_addrs.osd_name[..14].copy_from_slice(&name_buffer.buffer);
624 if self.tx_logical_address != LogicalAddress::Unregistered {
625 let message = Message::SetOsdName { name: name_buffer };
626 self.tx_message(&message, LogicalAddress::Tv)?;
627 }
628 Ok(())
629 }
630
631 pub fn get_vendor_id(&mut self) -> Result<Option<VendorId>> {
633 unsafe {
634 adapter_get_logical_addresses(self.file.as_raw_fd(), &mut self.internal_log_addrs)?;
635 }
636 VendorId::try_from_sys(self.internal_log_addrs.vendor_id)
637 }
638
639 pub fn set_vendor_id(&mut self, vendor_id: Option<VendorId>) -> Result<()> {
643 if let Some(vendor_id) = vendor_id {
644 self.internal_log_addrs.vendor_id = vendor_id.into();
645 #[cfg(feature = "tracing")]
646 debug!(
647 "Setting vendor ID to {:02X}-{:02X}-{:02X}",
648 vendor_id[0], vendor_id[1], vendor_id[2]
649 );
650 } else {
651 #[cfg(feature = "tracing")]
652 debug!("Clearing vendor ID");
653 self.internal_log_addrs.vendor_id = SysVendorId::default();
654 }
655 Ok(())
656 }
657
658 pub fn tx_message(&self, message: &Message, destination: LogicalAddress) -> Result<u32> {
662 let reply =
663 self.tx_rx_message(message, destination, Opcode::FeatureAbort, Timeout::NONE)?;
664 Ok(reply.sequence)
665 }
666
667 pub fn tx_raw_message(&self, message: &mut cec_msg) -> Result<()> {
669 unsafe {
670 transmit_message(self.file.as_raw_fd(), message)?;
671 }
672 Ok(())
673 }
674
675 pub fn tx_rx_message(
680 &self,
681 message: &Message,
682 destination: LogicalAddress,
683 reply: Opcode,
684 timeout: Timeout,
685 ) -> Result<Envelope> {
686 let mut raw_message = cec_msg::new(self.tx_logical_address.into(), destination.into());
687 let bytes = message.to_bytes();
688 let len = usize::min(bytes.len(), 15) + 1;
689 raw_message.len = len.try_into().unwrap();
690 raw_message.msg[1..len].copy_from_slice(&bytes[..len - 1]);
691 raw_message.reply = reply.into();
692 raw_message.timeout = timeout.as_ms();
693 #[cfg(feature = "tracing")]
694 debug!(
695 "Sending message {message:#?} to {destination} ({:x})",
696 destination as u8
697 );
698 self.tx_raw_message(&mut raw_message)?;
699 if !raw_message.tx_status.contains(CEC_TX_STATUS::OK) {
700 #[cfg(feature = "tracing")]
701 warn!("Message failed to send: {:?}", raw_message.tx_status);
702 return Err(raw_message.tx_status.into());
703 }
704 raw_message.try_into()
705 }
706
707 pub fn rx_message(&self, timeout: Timeout) -> Result<Envelope> {
711 self.rx_raw_message(timeout.as_ms())?.try_into()
712 }
713
714 pub fn rx_raw_message(&self, timeout_ms: u32) -> Result<cec_msg> {
716 let mut message = cec_msg::from_timeout(timeout_ms);
717 unsafe {
718 receive_message(self.file.as_raw_fd(), &mut message)?;
719 }
720 Ok(message)
721 }
722
723 pub fn poll_address(&self, destination: LogicalAddress) -> Result<()> {
729 let mut raw_message = cec_msg::new(self.tx_logical_address.into(), destination.into());
730 #[cfg(feature = "tracing")]
731 debug!("Sending poll to {destination} ({:x})", destination as u8);
732 self.tx_raw_message(&mut raw_message)?;
733 if !raw_message.tx_status.contains(CEC_TX_STATUS::OK) {
734 #[cfg(feature = "tracing")]
735 warn!("Poll failed: {:?}", raw_message.tx_status);
736 return Err(raw_message.tx_status.into());
737 }
738 Ok(())
739 }
740
741 pub(crate) fn dequeue_event(&self) -> Result<cec_event> {
742 let mut event = cec_event::default();
743 unsafe {
744 dequeue_event(self.file.as_raw_fd(), &mut event)?;
745 }
746 Ok(event)
747 }
748
749 pub(crate) fn get_mode(&self) -> Result<CecMessageHandlingMode> {
750 let mut mode = 0u32;
751 unsafe {
752 get_mode(self.file.as_raw_fd(), &mut mode)?;
753 }
754 Ok(mode.into())
755 }
756
757 pub(crate) fn set_mode(&self, mode: CecMessageHandlingMode) -> Result<()> {
758 unsafe {
759 set_mode(self.file.as_raw_fd(), &mode.into())?;
760 }
761 Ok(())
762 }
763
764 pub fn handle_status(&mut self, status: PollStatus) -> Result<Vec<PollResult>> {
769 let mut results = Vec::new();
770 if status.got_event() {
771 let ev = self.dequeue_event()?;
772 match ev.event {
773 CEC_EVENT_STATE_CHANGE => {
774 unsafe {
775 adapter_get_logical_addresses(
776 self.file.as_raw_fd(),
777 &mut self.internal_log_addrs,
778 )?;
779 }
780 if self.internal_log_addrs.num_log_addrs > 0 {
781 self.tx_logical_address =
782 LogicalAddress::try_from_primitive(self.internal_log_addrs.log_addr[0])
783 .unwrap_or(LogicalAddress::Unregistered);
784 } else {
785 self.tx_logical_address = LogicalAddress::Unregistered;
786 }
787 results.push(PollResult::StateChange);
788 }
789 CEC_EVENT_LOST_MSGS => results.push(PollResult::LostMessages(unsafe {
790 ev.data.lost_msgs.lost_msgs
791 })),
792 CEC_EVENT_PIN_CEC_LOW => results.push(PollResult::PinEvent(PinEvent {
793 pin: Pin::Cec,
794 state: PinState::Low,
795 })),
796 CEC_EVENT_PIN_CEC_HIGH => results.push(PollResult::PinEvent(PinEvent {
797 pin: Pin::Cec,
798 state: PinState::High,
799 })),
800 CEC_EVENT_PIN_HPD_LOW => results.push(PollResult::PinEvent(PinEvent {
801 pin: Pin::HotPlugDetect,
802 state: PinState::Low,
803 })),
804 CEC_EVENT_PIN_HPD_HIGH => results.push(PollResult::PinEvent(PinEvent {
805 pin: Pin::HotPlugDetect,
806 state: PinState::High,
807 })),
808 CEC_EVENT_PIN_5V_LOW => results.push(PollResult::PinEvent(PinEvent {
809 pin: Pin::Power5V,
810 state: PinState::Low,
811 })),
812 CEC_EVENT_PIN_5V_HIGH => results.push(PollResult::PinEvent(PinEvent {
813 pin: Pin::Power5V,
814 state: PinState::High,
815 })),
816 _ => return Err(Error::InvalidData),
817 }
818 }
819
820 if status.got_message() {
821 results.push(PollResult::Message(self.rx_message(Timeout::from_ms(1))?));
822 }
823
824 Ok(results)
825 }
826
827 pub fn get_connector_info(&self) -> Result<ConnectorInfo> {
830 let mut conn_info = cec_connector_info::default();
831 unsafe {
832 adapter_get_connector_info(self.file.as_raw_fd(), &mut conn_info)?;
833 }
834 match conn_info.ty {
835 CEC_CONNECTOR_TYPE_NO_CONNECTOR => Ok(ConnectorInfo::None),
836 CEC_CONNECTOR_TYPE_DRM => {
837 let cec_drm_connector_info {
838 card_no,
839 connector_id,
840 } = unsafe { conn_info.data.drm };
841 Ok(ConnectorInfo::DrmConnector {
842 card_no,
843 connector_id,
844 })
845 }
846 ty => Ok(ConnectorInfo::Unknown {
847 ty,
848 data: unsafe { conn_info.data.raw },
849 }),
850 }
851 }
852
853 pub fn set_active_source(&self, address: Option<PhysicalAddress>) -> Result<()> {
856 let address = match address {
857 Some(address) => address,
858 None => self.get_physical_address()?,
859 };
860 let active_source = Message::ActiveSource { address };
861 self.tx_message(&active_source, LogicalAddress::Broadcast)?;
862 Ok(())
863 }
864
865 pub fn wake(&self, set_active: bool, text_view: bool) -> Result<()> {
873 if text_view {
874 let text_view_on = Message::TextViewOn {};
875 self.tx_message(&text_view_on, LogicalAddress::Tv)?;
876 } else {
877 let image_view_on = Message::ImageViewOn {};
878 self.tx_message(&image_view_on, LogicalAddress::Tv)?;
879 }
880 if set_active {
881 let address = self.get_physical_address()?;
882 let active_source = Message::ActiveSource { address };
883 self.tx_message(&active_source, LogicalAddress::Broadcast)?;
884 }
885 Ok(())
886 }
887
888 pub fn standby(&self, target: LogicalAddress) -> Result<()> {
890 let standby = Message::Standby {};
891 self.tx_message(&standby, target)?;
892 Ok(())
893 }
894
895 pub fn press_user_control(&self, ui_command: UiCommand, target: LogicalAddress) -> Result<()> {
900 let user_control = Message::UserControlPressed { ui_command };
901 self.tx_message(&user_control, target)?;
902 Ok(())
903 }
904
905 pub fn release_user_control(&self, target: LogicalAddress) -> Result<()> {
910 let user_control = Message::UserControlReleased {};
911 self.tx_message(&user_control, target)?;
912 Ok(())
913 }
914}
915
916impl TryFrom<File> for Device {
917 type Error = Error;
918
919 fn try_from(file: File) -> Result<Device> {
920 let mut internal_log_addrs = cec_log_addrs::default();
921 unsafe {
922 adapter_get_logical_addresses(file.as_raw_fd(), &mut internal_log_addrs)?;
923 }
924 let tx_logical_address = if internal_log_addrs.num_log_addrs > 0 {
925 LogicalAddress::try_from_primitive(internal_log_addrs.log_addr[0]).unwrap_or_default()
926 } else {
927 LogicalAddress::Unregistered
928 };
929
930 Ok(Device {
931 file,
932 tx_logical_address,
933 internal_log_addrs,
934 })
935 }
936}
937
938impl DevicePoller {
939 pub fn poll(&self, timeout: PollTimeout) -> Result<PollStatus> {
943 let mut pollfd = [PollFd::new(
944 self.fd.as_fd(),
945 PollFlags::POLLPRI | PollFlags::POLLIN,
946 )];
947 let done = poll(&mut pollfd, timeout)?;
948
949 if done == 0 {
950 return Err(Error::Timeout);
951 }
952
953 match pollfd[0].revents() {
954 None => Ok(PollStatus::Nothing),
955 Some(flags) if flags.contains(PollFlags::POLLHUP) => Ok(PollStatus::Destroyed),
956 Some(flags) if flags.contains(PollFlags::POLLIN | PollFlags::POLLPRI) => {
957 Ok(PollStatus::GotAll)
958 }
959 Some(flags) if flags.contains(PollFlags::POLLIN) => Ok(PollStatus::GotMessage),
960 Some(flags) if flags.contains(PollFlags::POLLPRI) => Ok(PollStatus::GotEvent),
961 Some(_) => Err(Error::UnknownError(String::from(
962 "Polling error encountered",
963 ))),
964 }
965 }
966}
967
968impl PollStatus {
969 #[must_use]
970 pub fn got_message(&self) -> bool {
971 matches!(self, PollStatus::GotMessage | PollStatus::GotAll)
972 }
973
974 #[must_use]
975 pub fn got_event(&self) -> bool {
976 matches!(self, PollStatus::GotEvent | PollStatus::GotAll)
977 }
978}