#define DRIVER_VERSION "1.95" #define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>" #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 */
/* 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 */
/* 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. staticcharSET_CARRIER_FREQ[]={MCE_CMD_PORT_IR, MCE_CMD_SETIRCFS,0x00,0x00}; staticcharSET_TX_BITMASK[]={MCE_CMD_PORT_IR,MCE_CMD_SETIRTXPORTS,0x00}; staticcharSET_RX_TIMEOUT[]={MCE_CMD_PORT_IR, MCE_CMD_SETIRTIMEOUT,0x00,0x00}; staticcharSET_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
}
/* 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;
}
/* 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;
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;
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));
}
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) { /* *UnabletoclearRXhalt/stall. *Willneedtocallusb_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) { /* *UnabletoclearTXhalt/stall. *Willneedtocallusb_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);
/* 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.