From d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:45:28 +0000 Subject: [PATCH] add boot partition size --- kernel/drivers/media/rc/mceusb.c | 300 +++++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 195 insertions(+), 105 deletions(-) diff --git a/kernel/drivers/media/rc/mceusb.c b/kernel/drivers/media/rc/mceusb.c index 0b619a2..142319c 100644 --- a/kernel/drivers/media/rc/mceusb.c +++ b/kernel/drivers/media/rc/mceusb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for USB Windows Media Center Ed. eHome Infrared Transceivers * @@ -19,18 +20,6 @@ * 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 - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/device.h> @@ -80,7 +69,7 @@ #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 thru 0xdf flush & 0x1f bytes */ +#define MCE_PORT_SER 0x6 /* 0xc0 through 0xdf flush & 0x1f bytes */ #define MCE_PORT_MASK 0xe0 /* Mask out command bits */ /* Command port headers */ @@ -433,6 +422,15 @@ .driver_info = HAUPPAUGE_CX_HYBRID_TV }, { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb139), .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + /* Hauppauge WinTV-HVR-935C - based on cx231xx */ + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb151), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + /* Hauppauge WinTV-HVR-955Q - based on cx231xx */ + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb123), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + /* Hauppauge WinTV-HVR-975 - based on cx231xx */ + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb150), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, { USB_DEVICE(VENDOR_PCTV, 0x0259), .driver_info = HAUPPAUGE_CX_HYBRID_TV }, { USB_DEVICE(VENDOR_PCTV, 0x025e), @@ -464,6 +462,7 @@ /* usb */ struct usb_device *usbdev; + struct usb_interface *usbintf; struct urb *urb_in; unsigned int pipe_in; struct usb_endpoint_descriptor *usb_ep_out; @@ -520,6 +519,7 @@ unsigned long 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 */ @@ -564,7 +564,7 @@ datasize = 4; break; case MCE_CMD_G_REVISION: - datasize = 2; + datasize = 4; break; case MCE_RSP_EQWAKESUPPORT: case MCE_RSP_GETWAKESOURCE: @@ -600,14 +600,9 @@ char *inout; u8 cmd, subcmd, *data; struct device *dev = ir->dev; - int start, skip = 0; u32 carrier, period; - /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ - if (ir->flags.microsoft_gen1 && !out && !offset) - skip = 2; - - if (len <= skip) + if (offset < 0 || offset >= buf_len) return; dev_dbg(dev, "%cx data[%d]: %*ph (len=%d sz=%d)", @@ -616,11 +611,32 @@ inout = out ? "Request" : "Got"; - start = offset + skip; - cmd = buf[start] & 0xff; - subcmd = buf[start + 1] & 0xff; - data = buf + start + 2; + cmd = buf[offset]; + subcmd = (offset + 1 < buf_len) ? buf[offset + 1] : 0; + data = &buf[offset] + 2; + /* 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) @@ -644,7 +660,7 @@ dev_dbg(dev, "Get hw/sw rev?"); else dev_dbg(dev, "hw/sw rev %*ph", - 4, &buf[start + 2]); + 4, &buf[offset + 2]); break; case MCE_CMD_RESUME: dev_dbg(dev, "Device resume requested"); @@ -753,13 +769,6 @@ default: break; } - - if (cmd == MCE_IRDATA_TRAILER) - dev_dbg(dev, "End of raw IR data"); - else if ((cmd != MCE_CMD_PORT_IR) && - ((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA)) - dev_dbg(dev, "Raw IR data, %d pulse/space samples", - cmd & MCE_PACKET_LENGTH_MASK); #endif } @@ -772,8 +781,15 @@ static void 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; + } + if (!schedule_work(&ir->kevent)) - dev_err(ir->dev, "kevent %d may have been dropped", kevent); + dev_dbg(ir->dev, "kevent %d already scheduled", kevent); else dev_dbg(ir->dev, "kevent %d scheduled", kevent); } @@ -1061,7 +1077,7 @@ struct mceusb_dev *ir = dev->priv; unsigned int units; - units = DIV_ROUND_CLOSEST(timeout, US_TO_NS(MCE_TIME_UNIT)); + units = DIV_ROUND_UP(timeout, MCE_TIME_UNIT); cmdbuf[2] = units >> 8; cmdbuf[3] = units; @@ -1136,32 +1152,62 @@ } /* + * 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. */ -static void mceusb_handle_command(struct mceusb_dev *ir, int index) +static void mceusb_handle_command(struct mceusb_dev *ir, u8 *buf_in) { - DEFINE_IR_RAW_EVENT(rawir); - u8 hi = ir->buf_in[index + 1] & 0xff; - u8 lo = ir->buf_in[index + 2] & 0xff; + 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; - switch (ir->buf_in[index]) { - /* the one and only 5-byte return value command */ - case MCE_RSP_GETPORTSTATUS: - if ((ir->buf_in[index + 4] & 0xff) == 0x00) - ir->txports_cabled |= 1 << hi; - break; + 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 = US_TO_NS((hi << 8 | lo) * MCE_TIME_UNIT); + ir->rc->timeout = (*hi << 8 | *lo) * MCE_TIME_UNIT; break; case MCE_RSP_EQIRNUMPORTS: - ir->num_txports = hi; - ir->num_rxports = lo; + ir->num_txports = *hi; + ir->num_rxports = *lo; break; case MCE_RSP_EQIRRXCFCNT: /* @@ -1174,7 +1220,7 @@ */ if (ir->carrier_report_enabled && ir->learning_active && ir->pulse_tunit > 0) { - carrier_cycles = (hi << 8 | lo); + carrier_cycles = (*hi << 8 | *lo); /* * Adjust carrier cycle count by adding * 1 missed count per pulse "on" @@ -1192,24 +1238,24 @@ break; /* 1-byte return value commands */ - case MCE_RSP_EQEMVER: - ir->emver = hi; - break; case MCE_RSP_EQIRTXPORTS: - ir->tx_mask = hi; + ir->tx_mask = *hi; break; case MCE_RSP_EQIRRXPORTEN: - ir->learning_active = ((hi & 0x02) == 0x02); - if (ir->rxports_active != hi) { + ir->learning_active = ((*hi & 0x02) == 0x02); + if (ir->rxports_active != *hi) { dev_info(ir->dev, "%s-range (0x%x) receiver active", - ir->learning_active ? "short" : "long", hi); - ir->rxports_active = hi; + ir->learning_active ? "short" : "long", *hi); + ir->rxports_active = *hi; } break; + + /* No return value commands */ case MCE_RSP_CMD_ILLEGAL: case MCE_RSP_TX_TIMEOUT: ir->need_reset = true; break; + default: break; } @@ -1217,7 +1263,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) { - DEFINE_IR_RAW_EVENT(rawir); + struct ir_raw_event rawir = {}; bool event = false; int i = 0; @@ -1235,26 +1281,26 @@ ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]); mceusb_dev_printdata(ir, ir->buf_in, buf_len, i - 1, ir->rem + 2, false); - mceusb_handle_command(ir, i); + if (i + ir->rem < buf_len) + mceusb_handle_command(ir, &ir->buf_in[i - 1]); ir->parser_state = CMD_DATA; break; case PARSE_IRDATA: ir->rem--; - init_ir_raw_event(&rawir); rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK); if (unlikely(!rawir.duration)) { - dev_warn(ir->dev, "nonsensical irdata %02x with duration 0", - ir->buf_in[i]); + dev_dbg(ir->dev, "nonsensical irdata %02x with duration 0", + ir->buf_in[i]); break; } if (rawir.pulse) { ir->pulse_tunit += rawir.duration; ir->pulse_count++; } - rawir.duration *= US_TO_NS(MCE_TIME_UNIT); + rawir.duration *= MCE_TIME_UNIT; - dev_dbg(ir->dev, "Storing %s %u ns (%02x)", + dev_dbg(ir->dev, "Storing %s %u us (%02x)", rawir.pulse ? "pulse" : "space", rawir.duration, ir->buf_in[i]); @@ -1265,26 +1311,35 @@ ir->rem--; break; case CMD_HEADER: - /* decode mce packets of the form (84),AA,BB,CC,DD */ - /* IR data packets can span USB messages - rem */ ir->cmd = ir->buf_in[i]; if ((ir->cmd == MCE_CMD_PORT_IR) || ((ir->cmd & MCE_PORT_MASK) != MCE_COMMAND_IRDATA)) { + /* + * got PORT_SYS, PORT_IR, or unknown + * command response prefix + */ ir->parser_state = SUBCMD; continue; } + /* + * got IR data prefix (0x80 + num_bytes) + * decode MCE packets of the form {0x83, AA, BB, CC} + * IR data packets can span USB messages + */ ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK); mceusb_dev_printdata(ir, ir->buf_in, buf_len, i, ir->rem + 1, false); if (ir->rem) { ir->parser_state = PARSE_IRDATA; } else { - init_ir_raw_event(&rawir); - rawir.timeout = 1; - rawir.duration = ir->rc->timeout; + struct ir_raw_event ev = { + .timeout = 1, + .duration = ir->rc->timeout + }; + if (ir_raw_event_store_with_filter(ir->rc, - &rawir)) + &ev)) event = true; ir->pulse_tunit = 0; ir->pulse_count = 0; @@ -1295,6 +1350,14 @@ if (ir->parser_state != CMD_HEADER && !ir->rem) ir->parser_state = CMD_HEADER; } + + /* + * Accept IR data spanning multiple rx buffers. + * Reject MCE command response spanning multiple rx buffers. + */ + if (ir->parser_state != PARSE_IRDATA || !ir->rem) + ir->parser_state = CMD_HEADER; + if (event) { dev_dbg(ir->dev, "processed IR data"); ir_raw_event_handle(ir->rc); @@ -1353,42 +1416,37 @@ { int ret; struct device *dev = ir->dev; - char *data; - - data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL); - if (!data) { - dev_err(dev, "%s: memory allocation failed!", __func__); - return; - } + char data[USB_CTRL_MSG_SZ]; /* * 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(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), - USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0, - data, USB_CTRL_MSG_SZ, 3000); + 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(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), - USB_REQ_SET_FEATURE, USB_TYPE_VENDOR, - 0xc04e, 0x0000, NULL, 0, 3000); + ret = usb_control_msg_send(ir->usbdev, 0, + USB_REQ_SET_FEATURE, USB_TYPE_VENDOR, + 0xc04e, 0x0000, NULL, 0, 3000, GFP_KERNEL); dev_dbg(dev, "set feature - ret = %d", ret); /* bRequest 4: set char length to 8 bits */ - ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), - 4, USB_TYPE_VENDOR, - 0x0808, 0x0000, NULL, 0, 3000); + ret = usb_control_msg_send(ir->usbdev, 0, + 4, USB_TYPE_VENDOR, + 0x0808, 0x0000, NULL, 0, 3000, GFP_KERNEL); dev_dbg(dev, "set char length - retB = %d", ret); /* bRequest 2: set handshaking to use DTR/DSR */ - ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), - 2, USB_TYPE_VENDOR, - 0x0000, 0x0100, NULL, 0, 3000); + ret = usb_control_msg_send(ir->usbdev, 0, + 2, USB_TYPE_VENDOR, + 0x0000, 0x0100, NULL, 0, 3000, GFP_KERNEL); dev_dbg(dev, "set handshake - retC = %d", ret); /* device resume */ @@ -1396,8 +1454,6 @@ /* get hw/sw revision? */ mce_command_out(ir, GET_REVISION, sizeof(GET_REVISION)); - - kfree(data); } static void mceusb_gen2_init(struct mceusb_dev *ir) @@ -1465,28 +1521,59 @@ 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) { - dev_err(ir->dev, "rx clear halt error %d", - status); + /* + * 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); - if (status == 0) { - status = usb_submit_urb(ir->urb_in, GFP_KERNEL); - if (status < 0) { - dev_err(ir->dev, - "rx unhalt submit urb error %d", - status); - } + 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); - if (status < 0) - dev_err(ir->dev, "tx clear halt error %d", status); + 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); } } @@ -1519,8 +1606,8 @@ rc->dev.parent = dev; rc->priv = ir; rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - rc->min_timeout = US_TO_NS(MCE_TIME_UNIT); - rc->timeout = MS_TO_NS(100); + rc->min_timeout = MCE_TIME_UNIT; + rc->timeout = MS_TO_US(100); if (!mceusb_model[ir->model].broken_irtimeout) { rc->s_timeout = mceusb_set_timeout; rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; @@ -1640,7 +1727,7 @@ goto mem_alloc_fail; ir->pipe_in = pipe; - ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in); + ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_KERNEL, &ir->dma_in); if (!ir->buf_in) goto buf_in_alloc_fail; @@ -1648,6 +1735,7 @@ if (!ir->urb_in) goto urb_in_alloc_fail; + ir->usbintf = intf; ir->usbdev = usb_get_dev(dev); ir->dev = &intf->dev; ir->len_in = maxp; @@ -1669,7 +1757,7 @@ if (dev->descriptor.iManufacturer && usb_string(dev, dev->descriptor.iManufacturer, buf, sizeof(buf)) > 0) - strlcpy(name, buf, sizeof(name)); + strscpy(name, buf, sizeof(name)); if (dev->descriptor.iProduct && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0) @@ -1755,6 +1843,8 @@ struct usb_device *dev = interface_to_usbdev(intf); struct mceusb_dev *ir = usb_get_intfdata(intf); + dev_dbg(&intf->dev, "%s called", __func__); + usb_set_intfdata(intf, NULL); if (!ir) -- Gitblit v1.6.2