Skip to content

Commit

Permalink
- Patching the the wintvci-r2.fw firmware to skip over an internal ve…
Browse files Browse the repository at this point in the history
…rfication and increased the COR setup timeout from 15s to 60s.

  Both may help to recognize CAMs like the ACL 2.2 or similar.
- Added a the new module parameter "uf_triggers_submission" to modify the minimum data which triggers URB submissions to the CAM.
  The range is from 8..959, defaults to 8.This is required if many, but very small data packets are writen to the device and causes
  to many URB submissions. For minisatip 100 should be a good value.

Signed-off-by: siricco <cco@aon.at>
  • Loading branch information
siricco committed Feb 19, 2019
1 parent acfec36 commit 32960bf
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 43 deletions.
24 changes: 14 additions & 10 deletions wintv-ci-ci.c
Expand Up @@ -73,6 +73,11 @@ static int dummy_half_uframes = 1;
module_param(dummy_half_uframes, int, 0644);
MODULE_PARM_DESC(dummy_half_uframes, "Quirk to reliable bring the last 2 TS packets of each USB-transfer into the CAM (default:on).");

static int uf_triggers_submission = ISOC_MIN_UF_SUBMIT;

module_param(uf_triggers_submission, int, 0644);
MODULE_PARM_DESC(uf_triggers_submission, "Set the minimum data in USB microframes which triggera URB submission to the CAM (8..969, default:8).");

/* --- R I N G B U F F E R --- */

static void ts_rb_free(struct ep_info *ep)
Expand Down Expand Up @@ -323,13 +328,14 @@ static int ci_isoc_setup(struct ep_info *ep, struct usb_device *udev)

#define DUMMY_IN_FAST 1
#define DUMMY_IN_SLOW 0
static void ci_quirks_set_dummy_IN_num_packets(struct urb *urb, int fast) {
static void ci_quirks_set_dummy_IN_num_packets(struct urb *urb, int fast, bool quite) {
#define DUMMY_IN_FAST_UF 1
#define DUMMY_IN_SLOW_UF 4
int uframes = (fast) ? DUMMY_IN_FAST_UF : DUMMY_IN_SLOW_UF;
if (urb->number_of_packets != uframes) {
pr_info("*** Quirks : set dummy IN packets from %d to %d uframes\n",
urb->number_of_packets, uframes);
if (!quite)
pr_info("*** Quirks : set dummy IN packets from %d to %d uframes\n",
urb->number_of_packets, uframes);
urb->number_of_packets = uframes;
}
}
Expand All @@ -348,7 +354,7 @@ static void ci_quirks_set_defaults(struct ci_device *ci_dev) {
/* IN */
isoc = &ci_dev->ep_isoc_in.u.isoc;
urb = isoc->transfers[isoc->num_transfers].urb;/* the dummy IN URB */
ci_quirks_set_dummy_IN_num_packets(urb, DUMMY_IN_FAST);
ci_quirks_set_dummy_IN_num_packets(urb, DUMMY_IN_FAST, true);
}
}

Expand All @@ -373,7 +379,7 @@ static int ci_isoc_init(struct ci_device *ci_dev)

int num_transfers = min(ISOC_NUM_TRANSFERS, ISOC_MAX_TRANSFERS);
int num_uframes = min(ISOC_NUM_UFRAMES, ISOC_MAX_UFRAMES);
int min_submit_uf = min(ISOC_MIN_UF_SUBMIT, ISOC_MIN_UF_CHUNK);
int min_submit_uf = min(uf_triggers_submission, (num_uframes*num_transfers)-1);

/* OUT */
ep = &ci_dev->ep_isoc_out;
Expand Down Expand Up @@ -402,11 +408,10 @@ static int ci_isoc_init(struct ci_device *ci_dev)

void ts_stop_streaming(struct ci_device *ci_dev)
{
pr_info("%s : stop streaming\n", __func__);

ci_dev->isoc_enabled = 0;

if (ci_dev->isoc_urbs_running) {
pr_info("%s : stop streaming\n", __func__);
ci_isoc_kill_urbs(&ci_dev->ep_isoc_out);
ci_isoc_kill_urbs(&ci_dev->ep_isoc_in);
ci_dev->isoc_urbs_running = 0;
Expand Down Expand Up @@ -502,7 +507,7 @@ void ts_read_CAM_complete(struct urb *urb) /* CAM --> TS-IN ringbuffer */
if (ci_dev->isoc_urbs_running == 1 && ci_dev->isoc_TS_CAM < 0 &&
dummy_half_uframes && urb->number_of_packets == DUMMY_IN_FAST_UF) { /* more IN then OUT */
/* set for all following submisssions */
ci_quirks_set_dummy_IN_num_packets(urb, DUMMY_IN_SLOW);
ci_quirks_set_dummy_IN_num_packets(urb, DUMMY_IN_SLOW, false);
}
#if DEBUG_TS_IO
pr_info("%s(%d) : --- %d x TS, %2d uframes - rb-avail(%zu) CAM(%+d)\n",
Expand Down Expand Up @@ -863,8 +868,7 @@ void ci_reset(struct wintv_ci_dev *wintvci)
{
struct ci_device *ci_dev = &wintvci->ci_dev;

pr_info("Reset CI Device\n");

//pr_info("Reset CI Device\n");
ts_stop_streaming(ci_dev);
ci_quirks_set_defaults(ci_dev);

Expand Down
92 changes: 74 additions & 18 deletions wintv-ci-core.c
Expand Up @@ -75,6 +75,7 @@ static struct usb_id_info usb2_ci_info = {

#define USB_CTL_TIMEOUT 2000
#define USB_CMD_TIMEOUT 5000 /* 5 seconds */
#define USB_COR_TIMEOUT 60000 /* 60 seconds */

static int ezusb_ctrl_write(struct wintv_ci_dev *wintvci,
unsigned char request,
Expand Down Expand Up @@ -165,7 +166,7 @@ enum { /* commands and expected reply values */
CI_10_HW_RESET = 0x10 | 0x2,
CI_20_LPDU_WRITE = 0x20 | 0x0,
CI_30_undef = 0x30,
CI_40_GET_CIS = 0x40 | 0x3,
CI_40_GET_CIS = 0x40 | 0x3,
CI_50_STATUS = 0x50 | 0x5,
CI_60_NEGOTIATE = 0x60 | 0x4,
CI_70_WRITE_COR = 0x70 | 0x7,
Expand All @@ -188,6 +189,9 @@ enum { /* reply error values */
CI_ERR_ERROR_MASK = 0xF0
};

#define CI_CMD_SEND(c) (c & CI_CMD_SEND_MASK)
#define CI_CMD_REPLY(c) (c & CI_CMD_REPLY_MASK)

struct ci_cmd_info {
const char *name;
int on_state;
Expand All @@ -200,7 +204,7 @@ static struct ci_cmd_info CI_CMD_INFO[] = {
{ "CI_20_LPDU_WRITE", USBCI_STATE_RDY, 0 },
{ "CI_30_undef", 0,0 },
{ "CI_40_GET_CIS", USBCI_STATE_CIS, USBCI_STATE_COR },
{ "CI_50_STATUS", USBCI_STATE_RDY, 0 }, /* timeout if no link */
{ "CI_50_STATUS", USBCI_STATE_RDY, 0 }, /* times out without configured link */
{ "CI_60_NEGOTIATE", USBCI_STATE_LNK, USBCI_STATE_RDY },
{ "CI_70_WRITE_COR", USBCI_STATE_COR, USBCI_STATE_LNK },
{ "CI_80_LPDU_READ", USBCI_STATE_RDY, 0 },
Expand Down Expand Up @@ -291,12 +295,13 @@ static int CI_read_CMD_REPLY(struct wintv_ci_dev *wintvci, u8 CI_CMD_R,
int msg_len = -1;
int rem_size = CA_CTRL_MAXMSG;
int frag_len, transmitted, rc;

int timeout = CI_CMD_R == CI_CMD_REPLY(CI_70_WRITE_COR)
? 10000 + USB_COR_TIMEOUT : USB_CMD_TIMEOUT;
do {
rc = usb_interrupt_msg(udev, ep->pipe,
intr->pkt.buffer,
CA_CTRL_MAXPKT,
&transmitted, USB_CMD_TIMEOUT);
&transmitted, timeout);

if (rc || (intr->pkt.hdr->reply != CI_CMD_R)) {
if (intr->pkt.hdr->reply != CI_ERR_30_NO_CAM)
Expand Down Expand Up @@ -347,7 +352,7 @@ static int CI_read_CMD_REPLY(struct wintv_ci_dev *wintvci, u8 CI_CMD_R,

int cam_state_set(struct wintv_ci_dev *wintvci, int usbci_state)
{
if (!usbci_state || wintvci->slot.usbci_state == usbci_state)
if (wintvci->slot.usbci_state == usbci_state)
return 0;

pr_info("%-20s: %02X -> %02X\n",
Expand Down Expand Up @@ -379,8 +384,8 @@ static int CI_WriteExchange(struct wintv_ci_dev *wintvci, u8 CI_CMD,
struct msg_reply *reply,
char *data, int data_len) {

unsigned char CI_CMD_S = CI_CMD & CI_CMD_SEND_MASK;
unsigned char CI_CMD_R = CI_CMD & CI_CMD_REPLY_MASK;
unsigned char CI_CMD_S = CI_CMD_SEND(CI_CMD);
unsigned char CI_CMD_R = CI_CMD_REPLY(CI_CMD);

struct ci_cmd_info *cinfo = &CI_CMD_INFO[CI_CMD_S >> 4];

Expand All @@ -394,8 +399,8 @@ static int CI_WriteExchange(struct wintv_ci_dev *wintvci, u8 CI_CMD,

curr_state = wintvci->slot.usbci_state;
if (cinfo->on_state && (curr_state != cinfo->on_state)) {
pr_err("%s *** invalid usbci-state (%02X/%02X) ***\n", __func__,
curr_state, cinfo->on_state);
pr_err("%s *** invalid usbci-state (%02X : %02X/%02X) ***\n", __func__,
CI_CMD, curr_state, cinfo->on_state);
rc = CI_ERR_90_CMD_INV;
goto done;
}
Expand All @@ -416,10 +421,10 @@ static int CI_WriteExchange(struct wintv_ci_dev *wintvci, u8 CI_CMD,
t_response = jiffies_to_msecs(jiffies - t_start);

if (t_response > 50) /* typical 30ms - 33ms */
pr_info("%-20s: [%02X/%02X] response after %u ms\n",
pr_info("%-20s: [%02X/%02X] response time %u ms\n",
__func__, CI_CMD_S, CI_CMD_R, t_response);

if (rc && (rc != CI_ERR_30_NO_CAM)) /* FAILURE */
if (rc && rc != CI_ERR_30_NO_CAM) /* FAILURE */
pr_err("XR-rc(%d != %d) *** CAM-ERROR(%02X) ***\n",
rc, CI_CMD_R, CI_CMD_S);
done:
Expand Down Expand Up @@ -495,6 +500,7 @@ static int CI_60_Negotiate(struct wintv_ci_dev *wintvci,

buf[0] = (link_size >> 8) & 0xff; /* msb */
buf[1] = link_size & 0xff;

rc = CI_WriteExchange(wintvci, CI_60_NEGOTIATE, &reply, buf, sizeof buf);
if (!rc) {
nego_size = reply.buffer[0] << 8 |
Expand Down Expand Up @@ -565,7 +571,6 @@ int cam_state_monitor(struct wintv_ci_dev *wintvci)

case USBCI_STATE_LNK:
link_size = CA_LINK_LAYER_SIZE;
// link_size = 0xffff; /* returns a max. link-layer size of 0x400 (1024.) bytes */
rc = CI_60_Negotiate(wintvci, link_size);
if (rc)
break;
Expand All @@ -575,7 +580,6 @@ int cam_state_monitor(struct wintv_ci_dev *wintvci)
break;

default:
cam_state_set(wintvci, USBCI_STATE_NON);
break;
}

Expand Down Expand Up @@ -677,6 +681,57 @@ static int wintv_usb_ci_parse_firmware( const struct firmware *fw,
return 0;
}

static int wintv_usb_ci_apply_patch(struct wintv_ci_dev *wintvci, int addr,
char *old, char *new, int len )
{
char buf[USB_EP0_SIZE];
int rc;
/* verfify existing fe-code */
rc = ezusb_ctrl_read(wintvci, VR_A0_INTRAM, addr, buf, len) != len;
if (!rc)
rc = strncmp(buf, old, len);
if (!rc)
rc = ezusb_ctrl_write(wintvci, VR_A0_INTRAM, addr, new, len) != len;
return rc;
}

static int wintv_usb_ci_patch_firmware(struct wintv_ci_dev *wintvci,
char *fw_name)
{
int rc = 0;
/* 1. j_CI_70_WRITE_COR - ACL 2.2 */
#define ADR_P2030 0x2030
char p2030[2][4] = {{ 0x65,0x50,0x70,0x61 }, { 0xE4,0x00,0x00,0x00 }};
/* 2. j_CI_70_WRITE_COR - increase timeout (MatrixAir ?) */
#define CTMO (USB_COR_TIMEOUT)
#define CTMOH ((CTMO >> 8) & 0xFF)
#define CTMOL (CTMO & 0xFF)
#define ADR_P204A 0x204A
char p204A[2][4] = {{ 0x7D,0x98,0x7C,0x3A },{ 0x7D, CTMOL, 0x7C, CTMOH }};
/* ignore missing FREE bit */
#define ADR_P2064 0x2064
char p2064[2][2] = {{ 0x80,0x32 },{ 0x0D, 0x00 }};

#define FOR_FW "wintvci_r2.fw"
if (strcmp(fw_name, FOR_FW))
return 0;

pr_info("*** applying patches to firmware: %s ***\n", fw_name);
#define PARAM3(p) p[0], p[1], sizeof(p[1])
do {
rc = wintv_usb_ci_apply_patch(wintvci, ADR_P2030, PARAM3(p2030));
if (rc) break;
rc = wintv_usb_ci_apply_patch(wintvci, ADR_P204A, PARAM3(p204A));
if (rc) break;
rc = wintv_usb_ci_apply_patch(wintvci, ADR_P2064, PARAM3(p2064));
if (rc) break;
} while (0);
if(rc)
pr_info("* %s\n", "--- FAILED ---");

return -rc;
}

static int wintv_usb_ci_load_firmware( struct wintv_ci_dev *wintvci,
char *fw_name )
{
Expand Down Expand Up @@ -747,6 +802,9 @@ static int wintv_usb_ci_load_firmware( struct wintv_ci_dev *wintvci,
blocks_written++;
}
}
/////////////////////////////////////////////
wintv_usb_ci_patch_firmware(wintvci, fw_name);
/////////////////////////////////////////////
EZ_CPU_START(wintvci,wintvci->info->fx);

pr_info(" * %d firmware blocks written to internal RAM\n", blocks_written);
Expand Down Expand Up @@ -873,7 +931,6 @@ static int wintv_usb_ci_setup_endpoints(struct wintv_ci_dev *wintvci)
pr_err(" *ERR* unable to set configuration[%d]\n", CI_USB_CONFIGURATION);
return rc;
}
pr_info(" * current configuration:%d\n", udev->actconfig->desc.bConfigurationValue);

host_intf = wintvci->intf->cur_altsetting;
if (host_intf->desc.bNumEndpoints != 4) {
Expand All @@ -883,7 +940,6 @@ static int wintv_usb_ci_setup_endpoints(struct wintv_ci_dev *wintvci)
}

for (i = 0; i < host_intf->desc.bNumEndpoints; i++) {

struct usb_endpoint_descriptor *epd = &host_intf->endpoint[i].desc;

unsigned char type = (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
Expand Down Expand Up @@ -1073,10 +1129,10 @@ static int wintv_usb_ci_probe(struct usb_interface *intf,
goto error;

wintv_usb_ci_show_sw_info(wintvci);
/* START */
pr_err("probe succesfull\n");
cam_state_set(wintvci, USBCI_STATE_NON);
}

pr_err("probe succesfull\n");
return 0;
error:
usb_put_dev(wintvci->udev);
Expand Down Expand Up @@ -1145,7 +1201,7 @@ MODULE_DEVICE_TABLE (usb, wintv_usb_ci_table);

MODULE_AUTHOR("Helmut Binder");
MODULE_DESCRIPTION("Hauppauge WinTV-CI USB2 Common Interface driver");
MODULE_VERSION("0.3.0");
MODULE_VERSION("0.3.1");
MODULE_LICENSE("GPL");

static struct usb_driver wintv_usb_ci_driver = {
Expand Down
25 changes: 10 additions & 15 deletions wintv-ci-pcmcia.c
Expand Up @@ -77,7 +77,7 @@ int parse_cis(unsigned char *cis, int size, struct slot_info *s_info) {

memset(&c_v1,0,sizeof(c_v1));

// print_hex_dump(KERN_DEBUG, " CIS : ", DUMP_PREFIX_OFFSET, 16, 1,
// print_hex_dump(KERN_DEBUG, " CIS : ", DUMP_PREFIX_OFFSET, 16, 1,
// cis, size, 1);

for (i = 0, i_todo = size; i_todo; i = i_next) {
Expand Down Expand Up @@ -116,7 +116,7 @@ int parse_cis(unsigned char *cis, int size, struct slot_info *s_info) {

u8 cc_rasz = cis[i] & 0x3;
u8 cc_rmsz = (cis[i] >> 2) & 0xF; /* up to 16 bytes (128 bits) for Cfg-Reg-Present */

u8 cc_rfsz = (cis[i] >> 6) & 0x3; /* reserved size 0..3 bytes */
i += 2;

/* [4] RADR */
Expand All @@ -125,21 +125,15 @@ int parse_cis(unsigned char *cis, int size, struct slot_info *s_info) {
}
pr_info("%s : CFG_BASE: 0x%X (Cfg.Reg[0] in Attrib.Memory)\n",
__func__, cfg_base);

s_info->config_base = cfg_base;

for (n = 0; n <= cc_rmsz; n++, i++) {
pr_info("%s : CFG_REGS present [%d-%d] = 0x%02X\n",
__func__, n<<3, ((n+1)<<3)-1, cis[i]);
}
/* skip up to 3 zeros */
for (n = 0; n < 3; n++, i++)
if (cis[i])
break; /* none-zero */
#if CIS_DEBUG
if (n)
pr_info("%s : skipped %d zero-bytes\n",__func__, n);
#endif

i += cc_rfsz;/* skip up to 3 reserved bytes */

/* check sub-tuples for valid custom-interface with if-code 0x241 (DVB CI) */
cc_index = 0;
for (j = i, j_todo = i_next - i; j_todo; j = j_next, cc_index++) {
Expand Down Expand Up @@ -188,18 +182,19 @@ int parse_cis(unsigned char *cis, int size, struct slot_info *s_info) {

if (!s_info->cis_valid)
break; /* no vaild CI-card interface found (or 0x1A not parsed) */

if ((tpce_idx & 0x80) == 0)
continue; /* no cfg-byte */

if_type = cis[i+1] & 0xF;
if (if_type != (cc_index+4)) { /* custom interfaces start at 4 */
cfg_option = tpce_idx & 0x3F;

if (if_type != (cc_index+4) || /* custom interfaces start at 4 */
(cfg_option & 0x5) != 0x5) { /* only if enabled and use IRGs */
pr_info("%s : skip Interface description for IF-index %d, cfg-options 0x%X\n",
__func__, if_type, tpce_idx & 0x3F);
__func__, if_type, cfg_option);
continue;
}

cfg_option = tpce_idx & 0x3F;
if (!s_info->config_option || (tpce_idx & 0x40)) { /* store new or last default */
pr_info("%s : CFG-OPTIONS: 0x%X\n",
__func__, cfg_option);
Expand Down

0 comments on commit 32960bf

Please sign in to comment.