// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for USB Windows Media Center Ed. eHome Infrared Transceivers * * Copyright (c) 2010-2011, Jarod Wilson <jarod@redhat.com> * * Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan * Conti, Martin Blatter and Daniel Melander, the latter of which was * in turn also based on the lirc_atiusb driver by Paul Miller. The * two mce drivers were merged into one by Jarod Wilson, with transmit * support for the 1st-gen device added primarily by Patrick Calhoun, * with a bit of tweaks by Jarod. Debugging improvements and proper * support for what appears to be 3rd-gen hardware added by Jarod. * Initial port from lirc driver to ir-core drivery by Jarod, based * partially on a port to an earlier proposed IR infrastructure by * Jon Smirl, which included enhancements and simplifications to the * incoming IR buffer parsing routines. * * Updated in July of 2011 with the aid of Microsoft's official * remote/transceiver requirements and specification document, found at * download.microsoft.com, title * Windows-Media-Center-RC-IR-Collection-Green-Button-Specification-03-08-2011-V2.pdf
*/
#define DRIVER_VERSION "1.95" #define DRIVER_AUTHOR "Jarod Wilson " #define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \ "device driver" #define DRIVER_NAME "mceusb"
#define USB_TX_TIMEOUT 1000 /* in milliseconds */ #define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ #define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */
/* MCE constants */ #define MCE_IRBUF_SIZE 128 /* TX IR buffer length */ #define MCE_TIME_UNIT 50 /* Approx 50us resolution */ #define MCE_PACKET_SIZE 31 /* Max length of packet (with header) */ #define MCE_IRDATA_HEADER (0x80 + MCE_PACKET_SIZE - 1) /* Actual format is 0x80 + num_bytes */ #define MCE_IRDATA_TRAILER 0x80 /* End of IR data */ #define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */ #define MCE_DEFAULT_TX_MASK 0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */ #define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */ #define MCE_PULSE_MASK 0x7f /* Pulse mask */ #define MCE_MAX_PULSE_LENGTH 0x7f /* Longest transmittable pulse symbol */
/* * The interface between the host and the IR hardware is command-response * based. All commands and responses have a consistent format, where a lead * byte always identifies the type of data following it. The lead byte has * a port value in the 3 highest bits and a length value in the 5 lowest * bits. * * The length field is overloaded, with a value of 11111 indicating that the * following byte is a command or response code, and the length of the entire * message is determined by the code. If the length field is not 11111, then * it specifies the number of bytes of port data that follow.
*/ #define MCE_CMD 0x1f #define MCE_PORT_IR 0x4 /* (0x4 << 5) | MCE_CMD = 0x9f */ #define MCE_PORT_SYS 0x7 /* (0x7 << 5) | MCE_CMD = 0xff */ #define MCE_PORT_SER 0x6 /* 0xc0 through 0xdf flush & 0x1f bytes */ #define MCE_PORT_MASK 0xe0 /* Mask out command bits */
/* Commands that set device state (2-4 bytes in length) */ #define MCE_CMD_RESET 0xfe /* Reset device, 2 bytes */ #define MCE_CMD_RESUME 0xaa /* Resume device after error, 2 bytes */ #define MCE_CMD_SETIRCFS 0x06 /* Set tx carrier, 4 bytes */ #define MCE_CMD_SETIRTIMEOUT 0x0c /* Set timeout, 4 bytes */ #define MCE_CMD_SETIRTXPORTS 0x08 /* Set tx ports, 3 bytes */ #define MCE_CMD_SETIRRXPORTEN 0x14 /* Set rx ports, 3 bytes */ #define MCE_CMD_FLASHLED 0x23 /* Flash receiver LED, 2 bytes */
/* Commands that query device state (all 2 bytes, unless noted) */ #define MCE_CMD_GETIRCFS 0x07 /* Get carrier */ #define MCE_CMD_GETIRTIMEOUT 0x0d /* Get timeout */ #define MCE_CMD_GETIRTXPORTS 0x13 /* Get tx ports */ #define MCE_CMD_GETIRRXPORTEN 0x15 /* Get rx ports */ #define MCE_CMD_GETPORTSTATUS 0x11 /* Get tx port status, 3 bytes */ #define MCE_CMD_GETIRNUMPORTS 0x16 /* Get number of ports */ #define MCE_CMD_GETWAKESOURCE 0x17 /* Get wake source */ #define MCE_CMD_GETEMVER 0x22 /* Get emulator interface version */ #define MCE_CMD_GETDEVDETAILS 0x21 /* Get device details (em ver2 only) */ #define MCE_CMD_GETWAKESUPPORT 0x20 /* Get wake details (em ver2 only) */ #define MCE_CMD_GETWAKEVERSION 0x18 /* Get wake pattern (em ver2 only) */
/* Responses to error cases, must send MCE_CMD_RESUME to clear them */ #define MCE_RSP_CMD_ILLEGAL 0xfe /* illegal command for port, 2 bytes */ #define MCE_RSP_TX_TIMEOUT 0x81 /* tx timed out, 2 bytes */
/* Misc commands/responses not defined in the MCE remote/transceiver spec */ #define MCE_CMD_SIG_END 0x01 /* End of signal */ #define MCE_CMD_PING 0x03 /* Ping device */ #define MCE_CMD_UNKNOWN 0x04 /* Unknown */ #define MCE_CMD_UNKNOWN2 0x05 /* Unknown */ #define MCE_CMD_UNKNOWN3 0x09 /* Unknown */ #define MCE_CMD_UNKNOWN4 0x0a /* Unknown */ #define MCE_CMD_G_REVISION 0x0b /* Get hw/sw revision */ #define MCE_CMD_UNKNOWN5 0x0e /* Unknown */ #define MCE_CMD_UNKNOWN6 0x0f /* Unknown */ #define MCE_CMD_UNKNOWN8 0x19 /* Unknown */ #define MCE_CMD_UNKNOWN9 0x1b /* Unknown */ #define MCE_CMD_NULL 0x00 /* These show up various places... */
/* if buf[i] & MCE_PORT_MASK == 0x80 and buf[i] != MCE_CMD_PORT_IR,
* then we're looking at a raw IR data sample */ #define MCE_COMMAND_IRDATA 0x80 #define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
/* transmit support */
u32 carrier; unsignedchar tx_mask;
char phys[64]; enum mceusb_model_type model;
bool need_reset; /* flag to issue a device resume cmd */
u8 emver; /* emulator interface version */
u8 num_txports; /* number of transmit ports */
u8 num_rxports; /* number of receive sensors */
u8 txports_cabled; /* bitmask of transmitters with cable */
u8 rxports_active; /* bitmask of active receive sensors */ bool learning_active; /* wideband rx is active */
/* receiver carrier frequency detection support */
u32 pulse_tunit; /* IR pulse "on" cumulative time units */
u32 pulse_count; /* pulse "on" count in measurement interval */
/* * support for async error handler mceusb_deferred_kevent() * where usb_clear_halt(), usb_reset_configuration(), * usb_reset_device(), etc. must be done in process context
*/ struct work_struct kevent; unsignedlong kevent_flags; # define EVENT_TX_HALT 0 # define EVENT_RX_HALT 1 # define EVENT_RST_PEND 31
};
/* MCE Device Command Strings, generally a port and command pair */ staticchar DEVICE_RESUME[] = {MCE_CMD_NULL, MCE_CMD_PORT_SYS,
MCE_CMD_RESUME}; staticchar GET_REVISION[] = {MCE_CMD_PORT_SYS, MCE_CMD_G_REVISION}; staticchar GET_EMVER[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETEMVER}; staticchar GET_WAKEVERSION[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETWAKEVERSION}; staticchar FLASH_LED[] = {MCE_CMD_PORT_SYS, MCE_CMD_FLASHLED}; staticchar GET_UNKNOWN2[] = {MCE_CMD_PORT_IR, MCE_CMD_UNKNOWN2}; staticchar GET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRCFS}; staticchar GET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTIMEOUT}; staticchar GET_NUM_PORTS[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRNUMPORTS}; staticchar GET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTXPORTS}; staticchar GET_RX_SENSOR[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRRXPORTEN}; /* sub in desired values in lower byte or bytes for full command */ /* FIXME: make use of these for transmit. static char SET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR, MCE_CMD_SETIRCFS, 0x00, 0x00}; static char SET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00}; static char SET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR, MCE_CMD_SETIRTIMEOUT, 0x00, 0x00}; static char SET_RX_SENSOR[] = {MCE_CMD_PORT_IR, MCE_RSP_EQIRRXPORTEN, 0x00};
*/
staticint mceusb_cmd_datasize(u8 cmd, u8 subcmd)
{ int datasize = 0;
switch (cmd) { case MCE_CMD_NULL: if (subcmd == MCE_CMD_PORT_SYS)
datasize = 1; break; case MCE_CMD_PORT_SYS: switch (subcmd) { case MCE_RSP_GETPORTSTATUS:
datasize = 5; break; case MCE_RSP_EQWAKEVERSION:
datasize = 4; break; case MCE_CMD_G_REVISION:
datasize = 4; break; case MCE_RSP_EQWAKESUPPORT: case MCE_RSP_GETWAKESOURCE: case MCE_RSP_EQDEVDETAILS: case MCE_RSP_EQEMVER:
datasize = 1; break;
} break; case MCE_CMD_PORT_IR: switch (subcmd) { case MCE_CMD_UNKNOWN: case MCE_RSP_EQIRCFS: case MCE_RSP_EQIRTIMEOUT: case MCE_RSP_EQIRRXCFCNT: case MCE_RSP_EQIRNUMPORTS:
datasize = 2; break; case MCE_CMD_SIG_END: case MCE_RSP_EQIRTXPORTS: case MCE_RSP_EQIRRXPORTEN:
datasize = 1; break;
}
} return datasize;
}
/* Trace meaningless 0xb1 0x60 header bytes on original receiver */ if (ir->flags.microsoft_gen1 && !out && !offset) {
dev_dbg(dev, "MCE gen 1 header"); return;
}
/* Trace IR data header or trailer */ if (cmd != MCE_CMD_PORT_IR &&
(cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA) { if (cmd == MCE_IRDATA_TRAILER)
dev_dbg(dev, "End of raw IR data"); else
dev_dbg(dev, "Raw IR data, %d pulse/space samples",
cmd & MCE_PACKET_LENGTH_MASK); return;
}
/* Unexpected end of buffer? */ if (offset + len > buf_len) return;
/* Decode MCE command/response */ switch (cmd) { case MCE_CMD_NULL: if (subcmd == MCE_CMD_NULL) break; if ((subcmd == MCE_CMD_PORT_SYS) &&
(data[0] == MCE_CMD_RESUME))
dev_dbg(dev, "Device resume requested"); else
dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd); break; case MCE_CMD_PORT_SYS: switch (subcmd) { case MCE_RSP_EQEMVER: if (!out)
dev_dbg(dev, "Emulator interface version %x",
data[0]); break; case MCE_CMD_G_REVISION: if (len == 2)
dev_dbg(dev, "Get hw/sw rev?"); else
dev_dbg(dev, "hw/sw rev %4ph",
&buf[offset + 2]); break; case MCE_CMD_RESUME:
dev_dbg(dev, "Device resume requested"); break; case MCE_RSP_CMD_ILLEGAL:
dev_dbg(dev, "Illegal PORT_SYS command"); break; case MCE_RSP_EQWAKEVERSION: if (!out)
dev_dbg(dev, "Wake version, proto: 0x%02x, payload: 0x%02x, address: 0x%02x, version: 0x%02x",
data[0], data[1], data[2], data[3]); break; case MCE_RSP_GETPORTSTATUS: if (!out) /* We use data1 + 1 here, to match hw labels */
dev_dbg(dev, "TX port %d: blaster is%s connected",
data[0] + 1, data[3] ? " not" : ""); break; case MCE_CMD_FLASHLED:
dev_dbg(dev, "Attempting to flash LED"); break; default:
dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd); break;
} break; case MCE_CMD_PORT_IR: switch (subcmd) { case MCE_CMD_SIG_END:
dev_dbg(dev, "End of signal"); break; case MCE_CMD_PING:
dev_dbg(dev, "Ping"); break; case MCE_CMD_UNKNOWN:
dev_dbg(dev, "Resp to 9f 05 of 0x%02x 0x%02x",
data[0], data[1]); break; case MCE_RSP_EQIRCFS: if (!data[0] && !data[1]) {
dev_dbg(dev, "%s: no carrier", inout); break;
} // prescaler should make sense if (data[0] > 8) break;
period = DIV_ROUND_CLOSEST((1U << data[0] * 2) *
(data[1] + 1), 10); if (!period) break;
carrier = USEC_PER_SEC / period;
dev_dbg(dev, "%s carrier of %u Hz (period %uus)",
inout, carrier, period); break; case MCE_CMD_GETIRCFS:
dev_dbg(dev, "Get carrier mode and freq"); break; case MCE_RSP_EQIRTXPORTS:
dev_dbg(dev, "%s transmit blaster mask of 0x%02x",
inout, data[0]); break; case MCE_RSP_EQIRTIMEOUT: /* value is in units of 50us, so x*50/1000 ms */
period = ((data[0] << 8) | data[1]) *
MCE_TIME_UNIT / 1000;
dev_dbg(dev, "%s receive timeout of %d ms",
inout, period); break; case MCE_CMD_GETIRTIMEOUT:
dev_dbg(dev, "Get receive timeout"); break; case MCE_CMD_GETIRTXPORTS:
dev_dbg(dev, "Get transmit blaster mask"); break; case MCE_RSP_EQIRRXPORTEN:
dev_dbg(dev, "%s %s-range receive sensor in use",
inout, data[0] == 0x02 ? "short" : "long"); break; case MCE_CMD_GETIRRXPORTEN: /* aka MCE_RSP_EQIRRXCFCNT */ if (out)
dev_dbg(dev, "Get receive sensor"); else
dev_dbg(dev, "RX carrier cycle count: %d",
((data[0] << 8) | data[1])); break; case MCE_RSP_EQIRNUMPORTS: if (out) break;
dev_dbg(dev, "Num TX ports: %x, num RX ports: %x",
data[0], data[1]); break; case MCE_RSP_CMD_ILLEGAL:
dev_dbg(dev, "Illegal PORT_IR command"); break; case MCE_RSP_TX_TIMEOUT:
dev_dbg(dev, "IR TX timeout (TX buffer underrun)"); break; default:
dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd); break;
} break; default: break;
} #endif
}
/* * Schedule work that can't be done in interrupt handlers * (mceusb_dev_recv() and mce_write_callback()) nor BH work. * Invokes mceusb_deferred_kevent() for recovering from * error events specified by the kevent bit field.
*/ staticvoid mceusb_defer_kevent(struct mceusb_dev *ir, int kevent)
{
set_bit(kevent, &ir->kevent_flags);
if (test_bit(EVENT_RST_PEND, &ir->kevent_flags)) {
dev_dbg(ir->dev, "kevent %d dropped pending USB Reset Device",
kevent); return;
}
staticvoid mce_write_callback(struct urb *urb)
{ if (!urb) return;
complete(urb->context);
}
/* * Write (TX/send) data to MCE device USB endpoint out. * Used for IR blaster TX and MCE device commands. * * Return: The number of bytes written (> 0) or errno (< 0).
*/ staticint mce_write(struct mceusb_dev *ir, u8 *data, int size)
{ int ret; struct urb *urb; struct device *dev = ir->dev; unsignedchar *buf_out; struct completion tx_done; unsignedlong expire; unsignedlong ret_wait;
/* * Transmit IR out the MCE device IR blaster port(s). * * Convert IR pulse/space sequence from LIRC to MCE format. * Break up a long IR sequence into multiple parts (MCE IR data packets). * * u32 txbuf[] consists of IR pulse, space, ..., and pulse times in usec. * Pulses and spaces are implicit by their position. * The first IR sample, txbuf[0], is always a pulse. * * u8 irbuf[] consists of multiple IR data packets for the MCE device. * A packet is 1 u8 MCE_IRDATA_HEADER and up to 30 u8 IR samples. * An IR sample is 1-bit pulse/space flag with 7-bit time * in MCE time units (50usec). * * Return: The number of IR samples sent (> 0) or errno (< 0).
*/ staticint mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{ struct mceusb_dev *ir = dev->priv;
u8 cmdbuf[3] = { MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00 };
u8 irbuf[MCE_IRBUF_SIZE]; int ircount = 0; unsignedint irsample; int i, length, ret;
/* Send the set TX ports command */
cmdbuf[2] = ir->tx_mask;
mce_command_out(ir, cmdbuf, sizeof(cmdbuf));
/* Generate mce IR data packet */ for (i = 0; i < count; i++) {
irsample = txbuf[i] / MCE_TIME_UNIT;
/* loop to support long pulses/spaces > 6350us (127*50us) */ while (irsample > 0) { /* Insert IR header every 30th entry */ if (ircount % MCE_PACKET_SIZE == 0) { /* Room for IR header and one IR sample? */ if (ircount >= MCE_IRBUF_SIZE - 1) { /* Send near full buffer */
ret = mce_write(ir, irbuf, ircount); if (ret < 0) return ret;
ircount = 0;
}
irbuf[ircount++] = MCE_IRDATA_HEADER;
}
/* Insert IR sample */ if (irsample <= MCE_MAX_PULSE_LENGTH) {
irbuf[ircount] = irsample;
irsample = 0;
} else {
irbuf[ircount] = MCE_MAX_PULSE_LENGTH;
irsample -= MCE_MAX_PULSE_LENGTH;
} /* * Even i = IR pulse * Odd i = IR space
*/
irbuf[ircount] |= (i & 1 ? 0 : MCE_PULSE_BIT);
ircount++;
/* IR buffer full? */ if (ircount >= MCE_IRBUF_SIZE) { /* Fix packet length in last header */
length = ircount % MCE_PACKET_SIZE; if (length > 0)
irbuf[ircount - length] -=
MCE_PACKET_SIZE - length; /* Send full buffer */
ret = mce_write(ir, irbuf, ircount); if (ret < 0) return ret;
ircount = 0;
}
}
} /* after for loop, 0 <= ircount < MCE_IRBUF_SIZE */
/* Fix packet length in last header */
length = ircount % MCE_PACKET_SIZE; if (length > 0)
irbuf[ircount - length] -= MCE_PACKET_SIZE - length;
/* Append IR trailer (0x80) to final partial (or empty) IR buffer */
irbuf[ircount++] = MCE_IRDATA_TRAILER;
/* Send final buffer */
ret = mce_write(ir, irbuf, ircount); if (ret < 0) return ret;
return count;
}
/* Sets active IR outputs -- mce devices typically have two */ staticint mceusb_set_tx_mask(struct rc_dev *dev, u32 mask)
{ struct mceusb_dev *ir = dev->priv;
/* return number of transmitters */ int emitters = ir->num_txports ? ir->num_txports : 2;
/* get receiver timeout value */
mce_command_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT));
return 0;
}
/* * Select or deselect the 2nd receiver port. * Second receiver is learning mode, wide-band, short-range receiver. * Only one receiver (long or short range) may be active at a time.
*/ staticint mceusb_set_rx_wideband(struct rc_dev *dev, int enable)
{ struct mceusb_dev *ir = dev->priv; unsignedchar cmdbuf[3] = { MCE_CMD_PORT_IR,
MCE_CMD_SETIRRXPORTEN, 0x00 };
dev_dbg(ir->dev, "select %s-range receive sensor",
enable ? "short" : "long"); if (enable) {
ir->wideband_rx_enabled = true;
cmdbuf[2] = 2; /* port 2 is short range receiver */
} else {
ir->wideband_rx_enabled = false;
cmdbuf[2] = 1; /* port 1 is long range receiver */
}
mce_command_out(ir, cmdbuf, sizeof(cmdbuf)); /* response from device sets ir->learning_active */
return 0;
}
/* * Enable/disable receiver carrier frequency pass through reporting. * Only the short-range receiver has carrier frequency measuring capability. * Implicitly select this receiver when enabling carrier frequency reporting.
*/ staticint mceusb_set_rx_carrier_report(struct rc_dev *dev, int enable)
{ struct mceusb_dev *ir = dev->priv; unsignedchar cmdbuf[3] = { MCE_CMD_PORT_IR,
MCE_CMD_SETIRRXPORTEN, 0x00 };
dev_dbg(ir->dev, "%s short-range receiver carrier reporting",
enable ? "enable" : "disable"); if (enable) {
ir->carrier_report_enabled = true; if (!ir->learning_active) {
cmdbuf[2] = 2; /* port 2 is short range receiver */
mce_command_out(ir, cmdbuf, sizeof(cmdbuf));
}
} else {
ir->carrier_report_enabled = false; /* * Revert to normal (long-range) receiver only if the * wideband (short-range) receiver wasn't explicitly * enabled.
*/ if (ir->learning_active && !ir->wideband_rx_enabled) {
cmdbuf[2] = 1; /* port 1 is long range receiver */
mce_command_out(ir, cmdbuf, sizeof(cmdbuf));
}
}
return 0;
}
/* * Handle PORT_SYS/IR command response received from the MCE device. * * Assumes single response with all its data (not truncated) * in buf_in[]. The response itself determines its total length * (mceusb_cmd_datasize() + 2) and hence the minimum size of buf_in[]. * * We don't do anything but print debug spew for many of the command bits * we receive from the hardware, but some of them are useful information * we want to store so that we can use them.
*/ staticvoid mceusb_handle_command(struct mceusb_dev *ir, u8 *buf_in)
{
u8 cmd = buf_in[0];
u8 subcmd = buf_in[1];
u8 *hi = &buf_in[2]; /* read only when required */
u8 *lo = &buf_in[3]; /* read only when required */ struct ir_raw_event rawir = {};
u32 carrier_cycles;
u32 cycles_fix;
if (cmd == MCE_CMD_PORT_SYS) { switch (subcmd) { /* the one and only 5-byte return value command */ case MCE_RSP_GETPORTSTATUS: if (buf_in[5] == 0 && *hi < 8)
ir->txports_cabled |= 1 << *hi; break;
/* 1-byte return value commands */ case MCE_RSP_EQEMVER:
ir->emver = *hi; break;
/* No return value commands */ case MCE_RSP_CMD_ILLEGAL:
ir->need_reset = true; break;
default: break;
}
return;
}
if (cmd != MCE_CMD_PORT_IR) return;
switch (subcmd) { /* 2-byte return value commands */ case MCE_RSP_EQIRTIMEOUT:
ir->rc->timeout = (*hi << 8 | *lo) * MCE_TIME_UNIT; break; case MCE_RSP_EQIRNUMPORTS:
ir->num_txports = *hi;
ir->num_rxports = *lo; break; case MCE_RSP_EQIRRXCFCNT: /* * The carrier cycle counter can overflow and wrap around * without notice from the device. So frequency measurement * will be inaccurate with long duration IR. * * The long-range (non learning) receiver always reports * zero count so we always ignore its report.
*/ if (ir->carrier_report_enabled && ir->learning_active &&
ir->pulse_tunit > 0) {
carrier_cycles = (*hi << 8 | *lo); /* * Adjust carrier cycle count by adding * 1 missed count per pulse "on"
*/
cycles_fix = ir->flags.rx2 == 2 ? ir->pulse_count : 0;
rawir.carrier_report = 1;
rawir.carrier = (1000000u / MCE_TIME_UNIT) *
(carrier_cycles + cycles_fix) /
ir->pulse_tunit;
dev_dbg(ir->dev, "RX carrier frequency %u Hz (pulse count = %u, cycles = %u, duration = %u, rx2 = %u)",
rawir.carrier, ir->pulse_count, carrier_cycles,
ir->pulse_tunit, ir->flags.rx2);
ir_raw_event_store(ir->rc, &rawir);
} break;
case -ECONNRESET: case -ENOENT: case -EILSEQ: case -EPROTO: case -ESHUTDOWN:
usb_unlink_urb(urb); return;
case -EPIPE:
dev_err(ir->dev, "Error: urb status = %d (RX HALT)",
urb->status);
mceusb_defer_kevent(ir, EVENT_RX_HALT); return;
default:
dev_err(ir->dev, "Error: urb status = %d", urb->status); break;
}
usb_submit_urb(urb, GFP_ATOMIC);
}
staticvoid mceusb_get_emulator_version(struct mceusb_dev *ir)
{ /* If we get no reply or an illegal command reply, its ver 1, says MS */
ir->emver = 1;
mce_command_out(ir, GET_EMVER, sizeof(GET_EMVER));
}
/* * This is a strange one. Windows issues a set address to the device * on the receive control pipe and expect a certain value pair back
*/
ret = usb_control_msg_recv(ir->usbdev, 0, USB_REQ_SET_ADDRESS,
USB_DIR_IN | USB_TYPE_VENDOR,
0, 0, data, USB_CTRL_MSG_SZ, 3000,
GFP_KERNEL);
dev_dbg(dev, "set address - ret = %d", ret);
dev_dbg(dev, "set address - data[0] = %d, data[1] = %d",
data[0], data[1]);
/* set feature: bit rate 38400 bps */
ret = usb_control_msg_send(ir->usbdev, 0,
USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
0xc04e, 0x0000, NULL, 0, 3000, GFP_KERNEL);
/* * Workqueue function * for resetting or recovering device after occurrence of error events * specified in ir->kevent bit field. * Function runs (via schedule_work()) in non-interrupt context, for * calls here (such as usb_clear_halt()) requiring non-interrupt context.
*/ staticvoid mceusb_deferred_kevent(struct work_struct *work)
{ struct mceusb_dev *ir =
container_of(work, struct mceusb_dev, kevent); int status;
dev_err(ir->dev, "kevent handler called (flags 0x%lx)",
ir->kevent_flags);
if (test_bit(EVENT_RST_PEND, &ir->kevent_flags)) {
dev_err(ir->dev, "kevent handler canceled pending USB Reset Device"); return;
}
if (test_bit(EVENT_RX_HALT, &ir->kevent_flags)) {
usb_unlink_urb(ir->urb_in);
status = usb_clear_halt(ir->usbdev, ir->pipe_in);
dev_err(ir->dev, "rx clear halt status = %d", status); if (status < 0) { /* * Unable to clear RX halt/stall. * Will need to call usb_reset_device().
*/
dev_err(ir->dev, "stuck RX HALT state requires USB Reset Device to clear");
usb_queue_reset_device(ir->usbintf);
set_bit(EVENT_RST_PEND, &ir->kevent_flags);
clear_bit(EVENT_RX_HALT, &ir->kevent_flags);
/* Cancel all other error events and handlers */
clear_bit(EVENT_TX_HALT, &ir->kevent_flags); return;
}
clear_bit(EVENT_RX_HALT, &ir->kevent_flags);
status = usb_submit_urb(ir->urb_in, GFP_KERNEL); if (status < 0) {
dev_err(ir->dev, "rx unhalt submit urb error = %d",
status);
}
}
if (test_bit(EVENT_TX_HALT, &ir->kevent_flags)) {
status = usb_clear_halt(ir->usbdev, ir->pipe_out);
dev_err(ir->dev, "tx clear halt status = %d", status); if (status < 0) { /* * Unable to clear TX halt/stall. * Will need to call usb_reset_device().
*/
dev_err(ir->dev, "stuck TX HALT state requires USB Reset Device to clear");
usb_queue_reset_device(ir->usbintf);
set_bit(EVENT_RST_PEND, &ir->kevent_flags);
clear_bit(EVENT_TX_HALT, &ir->kevent_flags);
/* Cancel all other error events and handlers */
clear_bit(EVENT_RX_HALT, &ir->kevent_flags); return;
}
clear_bit(EVENT_TX_HALT, &ir->kevent_flags);
}
}
/* There are multi-function devices with non-IR interfaces */ if (idesc->desc.bInterfaceNumber != ir_intfnum) return -ENODEV;
/* step through the endpoints to find first bulk in and out endpoint */ for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
ep = &idesc->endpoint[i].desc;
/* Saving usb interface data for use by the transmitter routine */
ir->usb_ep_out = ep_out; if (usb_endpoint_xfer_int(ep_out))
ir->pipe_out = usb_sndintpipe(ir->usbdev,
ep_out->bEndpointAddress); else
ir->pipe_out = usb_sndbulkpipe(ir->usbdev,
ep_out->bEndpointAddress);
/* * Initialize async USB error handler before registering * or activating any mceusb RX and TX functions
*/
INIT_WORK(&ir->kevent, mceusb_deferred_kevent);
ir->rc = mceusb_init_rc_dev(ir); if (!ir->rc) goto rc_dev_fail;
/* wire up inbound data handler */ if (usb_endpoint_xfer_int(ep_in))
usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
mceusb_dev_recv, ir, ep_in->bInterval); else
usb_fill_bulk_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
mceusb_dev_recv, ir);
/* flush buffers on the device */
dev_dbg(&intf->dev, "Flushing receive buffers");
res = usb_submit_urb(ir->urb_in, GFP_KERNEL); if (res)
dev_err(&intf->dev, "failed to flush buffers: %d", res);
/* figure out which firmware/emulator version this hardware has */
mceusb_get_emulator_version(ir);
/* initialize device */ if (ir->flags.microsoft_gen1)
mceusb_gen1_init(ir); elseif (!is_gen3)
mceusb_gen2_init(ir);
mceusb_get_parameters(ir);
mceusb_flash_led(ir);
if (!ir->flags.no_tx)
mceusb_set_tx_mask(ir->rc, MCE_DEFAULT_TX_MASK);
usb_set_intfdata(intf, ir);
/* enable wake via this device */
device_set_wakeup_capable(ir->dev, true);
device_set_wakeup_enable(ir->dev, true);
dev_info(&intf->dev, "Registered %s with mce emulator interface version %x",
name, ir->emver);
dev_info(&intf->dev, "%x tx ports (0x%x cabled) and %x rx sensors (0x%x active)",
ir->num_txports, ir->txports_cabled,
ir->num_rxports, ir->rxports_active);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.