📄 pvrusb2-hdw.c
字号:
},{ .desc = "Video Standard Name", .name = "video_standard", .internal_id = PVR2_CID_STDENUM, .skip_init = !0, .get_value = ctrl_stdenumcur_get, .set_value = ctrl_stdenumcur_set, .is_dirty = ctrl_stdenumcur_is_dirty, .clear_dirty = ctrl_stdenumcur_clear_dirty, .type = pvr2_ctl_enum, }};#define CTRLDEF_COUNT ARRAY_SIZE(control_defs)const char *pvr2_config_get_name(enum pvr2_config cfg){ switch (cfg) { case pvr2_config_empty: return "empty"; case pvr2_config_mpeg: return "mpeg"; case pvr2_config_vbi: return "vbi"; case pvr2_config_pcm: return "pcm"; case pvr2_config_rawvideo: return "raw video"; } return "<unknown>";}struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw){ return hdw->usb_dev;}unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw){ return hdw->serial_number;}const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw){ return hdw->bus_info;}unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw){ return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;}/* Set the currently tuned frequency and account for all possible driver-core side effects of this action. */void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val){ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { if (hdw->freqSelector) { /* Swing over to radio frequency selection */ hdw->freqSelector = 0; hdw->freqDirty = !0; } if (hdw->freqValRadio != val) { hdw->freqValRadio = val; hdw->freqSlotRadio = 0; hdw->freqDirty = !0; } } else { if (!(hdw->freqSelector)) { /* Swing over to television frequency selection */ hdw->freqSelector = 1; hdw->freqDirty = !0; } if (hdw->freqValTelevision != val) { hdw->freqValTelevision = val; hdw->freqSlotTelevision = 0; hdw->freqDirty = !0; } }}int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw){ return hdw->unit_number;}/* Attempt to locate one of the given set of files. Messages are logged appropriate to what has been found. The return value will be 0 or greater on success (it will be the index of the file name found) and fw_entry will be filled in. Otherwise a negative error is returned on failure. If the return value is -ENOENT then no viable firmware file could be located. */static int pvr2_locate_firmware(struct pvr2_hdw *hdw, const struct firmware **fw_entry, const char *fwtypename, unsigned int fwcount, const char *fwnames[]){ unsigned int idx; int ret = -EINVAL; for (idx = 0; idx < fwcount; idx++) { ret = request_firmware(fw_entry, fwnames[idx], &hdw->usb_dev->dev); if (!ret) { trace_firmware("Located %s firmware: %s;" " uploading...", fwtypename, fwnames[idx]); return idx; } if (ret == -ENOENT) continue; pvr2_trace(PVR2_TRACE_ERROR_LEGS, "request_firmware fatal error with code=%d",ret); return ret; } pvr2_trace(PVR2_TRACE_ERROR_LEGS, "***WARNING***" " Device %s firmware" " seems to be missing.", fwtypename); pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Did you install the pvrusb2 firmware files" " in their proper location?"); if (fwcount == 1) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "request_firmware unable to locate %s file %s", fwtypename,fwnames[0]); } else { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "request_firmware unable to locate" " one of the following %s files:", fwtypename); for (idx = 0; idx < fwcount; idx++) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "request_firmware: Failed to find %s", fwnames[idx]); } } return ret;}/* * pvr2_upload_firmware1(). * * Send the 8051 firmware to the device. After the upload, arrange for * device to re-enumerate. * * NOTE : the pointer to the firmware data given by request_firmware() * is not suitable for an usb transaction. * */static int pvr2_upload_firmware1(struct pvr2_hdw *hdw){ const struct firmware *fw_entry = NULL; void *fw_ptr; unsigned int pipe; int ret; u16 address; static const char *fw_files_29xxx[] = { "v4l-pvrusb2-29xxx-01.fw", }; static const char *fw_files_24xxx[] = { "v4l-pvrusb2-24xxx-01.fw", }; static const struct pvr2_string_table fw_file_defs[] = { [PVR2_HDW_TYPE_29XXX] = { fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx) }, [PVR2_HDW_TYPE_24XXX] = { fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx) }, }; if ((hdw->hdw_type >= ARRAY_SIZE(fw_file_defs)) || (!fw_file_defs[hdw->hdw_type].lst)) { hdw->fw1_state = FW1_STATE_OK; return 0; } hdw->fw1_state = FW1_STATE_FAILED; // default result trace_firmware("pvr2_upload_firmware1"); ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller", fw_file_defs[hdw->hdw_type].cnt, fw_file_defs[hdw->hdw_type].lst); if (ret < 0) { if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING; return ret; } usb_settoggle(hdw->usb_dev, 0 & 0xf, !(0 & USB_DIR_IN), 0); usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); pipe = usb_sndctrlpipe(hdw->usb_dev, 0); if (fw_entry->size != 0x2000){ pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size"); release_firmware(fw_entry); return -ENOMEM; } fw_ptr = kmalloc(0x800, GFP_KERNEL); if (fw_ptr == NULL){ release_firmware(fw_entry); return -ENOMEM; } /* We have to hold the CPU during firmware upload. */ pvr2_hdw_cpureset_assert(hdw,1); /* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes chunk. */ ret = 0; for(address = 0; address < fw_entry->size; address += 0x800) { memcpy(fw_ptr, fw_entry->data + address, 0x800); ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address, 0, fw_ptr, 0x800, HZ); } trace_firmware("Upload done, releasing device's CPU"); /* Now release the CPU. It will disconnect and reconnect later. */ pvr2_hdw_cpureset_assert(hdw,0); kfree(fw_ptr); release_firmware(fw_entry); trace_firmware("Upload done (%d bytes sent)",ret); /* We should have written 8192 bytes */ if (ret == 8192) { hdw->fw1_state = FW1_STATE_RELOAD; return 0; } return -EIO;}/* * pvr2_upload_firmware2() * * This uploads encoder firmware on endpoint 2. * */int pvr2_upload_firmware2(struct pvr2_hdw *hdw){ const struct firmware *fw_entry = NULL; void *fw_ptr; unsigned int pipe, fw_len, fw_done, bcnt, icnt; int actual_length; int ret = 0; int fwidx; static const char *fw_files[] = { CX2341X_FIRM_ENC_FILENAME, }; if ((hdw->hdw_type != PVR2_HDW_TYPE_29XXX) && (hdw->hdw_type != PVR2_HDW_TYPE_24XXX)) { return 0; } trace_firmware("pvr2_upload_firmware2"); ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder", ARRAY_SIZE(fw_files), fw_files); if (ret < 0) return ret; fwidx = ret; ret = 0; /* Since we're about to completely reinitialize the encoder, invalidate our cached copy of its configuration state. Next time we configure the encoder, then we'll fully configure it. */ hdw->enc_cur_valid = 0; hdw->flag_encoder_ok = 0; /* First prepare firmware loading */ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ ret |= pvr2_hdw_cmd_deep_reset(hdw); ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/ ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/ ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/ ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/ ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/ ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ LOCK_TAKE(hdw->ctl_lock); do { hdw->cmd_buffer[0] = FX2CMD_FWPOST1; ret |= pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); hdw->cmd_buffer[0] = FX2CMD_MEMSEL; hdw->cmd_buffer[1] = 0; ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload prep failed, ret=%d",ret); release_firmware(fw_entry); return ret; } /* Now send firmware */ fw_len = fw_entry->size; if (fw_len % sizeof(u32)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "size of %s firmware" " must be a multiple of %zu bytes", fw_files[fwidx],sizeof(u32)); release_firmware(fw_entry); return -1; } fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); if (fw_ptr == NULL){ release_firmware(fw_entry); pvr2_trace(PVR2_TRACE_ERROR_LEGS, "failed to allocate memory for firmware2 upload"); return -ENOMEM; } pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); fw_done = 0; for (fw_done = 0; fw_done < fw_len;) { bcnt = fw_len - fw_done; if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE; memcpy(fw_ptr, fw_entry->data + fw_done, bcnt); /* Usbsnoop log shows that we must swap bytes... */ for (icnt = 0; icnt < bcnt/4 ; icnt++) ((u32 *)fw_ptr)[icnt] = ___swab32(((u32 *)fw_ptr)[icnt]); ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt, &actual_length, HZ); ret |= (actual_length != bcnt); if (ret) break; fw_done += bcnt; } trace_firmware("upload of %s : %i / %i ", fw_files[fwidx],fw_done,fw_len); kfree(fw_ptr); release_firmware(fw_entry); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload transfer failure"); return ret; } /* Finish upload */ ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ LOCK_TAKE(hdw->ctl_lock); do { hdw->cmd_buffer[0] = FX2CMD_MEMSEL; hdw->cmd_buffer[1] = 0; ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); } else { hdw->flag_encoder_ok = !0; hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE); } return ret;}#define FIRMWARE_RECOVERY_BITS \ ((1<<PVR2_SUBSYS_B_ENC_CFG) | \ (1<<PVR2_SUBSYS_B_ENC_RUN) | \ (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \ (1<<PVR2_SUBSYS_B_USBSTREAM_RUN))/* This single function is key to pretty much everything. The pvrusb2 device can logically be viewed as a series of subsystems which can be stopped / started or unconfigured / configured. To get things streaming, one must configure everything and start everything, but there may be various reasons over time to deconfigure something or stop something. This function handles all of this activity. Everything EVERYWHERE that must affect a subsystem eventually comes here to do the work. The current state of all subsystems is represented by a single bit mask, known as subsys_enabled_mask. The bit positions are defined by the PVR2_SUBSYS_xxxx macros, with one subsystem per bit position. At any time the set of configured or active subsystems can be queried just by looking at that mask. To change bits in that mask, this function here must be called. The "msk" argument indicates which bit positions to change, and the "val" argument defines the new values for the positions defined by "msk". There is a priority ordering of starting / stopping things, and for multiple requested changes, this function implements that ordering. (Thus we will act on a request to load encoder firmware before we configure the encoder.) In addition to priority ordering, there is a recovery strategy implemented here. If a particular step fails and we detect that failure, this function will clear the affected subsystem bits and restart. Thus we have a means for recovering from a dead encoder: Clear all bits that correspond to subsystems that we need to restart / reconfigure and start over.*/static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, unsigned long msk, unsigned long val){ unsigned long nmsk; unsigned long vmsk; int ret; unsigned int tryCount = 0; if (!hdw->flag_ok) return; msk &= PVR2_SUBSYS_ALL; nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk); nmsk &= PVR2_SUBSYS_ALL; for (;;) { tryCount++; if (!((nmsk ^ hdw->subsys_enabled_mask) & PVR2_SUBSYS_ALL)) break; if (tryCount > 4) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Too many retries when configuring device;" " giving up"); pvr2_hdw_render_useless(hdw); break; } if (tryCount > 1) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Retrying device reconfiguration"); } pvr2_trace(PVR2_TRACE_INIT, "subsys mask changing 0x%lx:0x%lx" " from 0x%lx to 0x%lx", msk,val,hdw->subsys_enabled_mask,nmsk); vmsk = (nmsk ^ hdw->subsys_enabled_mask) & hdw->subsys_enabled_mask; if (vmsk) { if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_encoder_stop"); ret = pvr2_encoder_stop(hdw); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Error recovery initiated"); hdw->subsys_enabled_mask &= ~FIRMWARE_RECOVERY_BITS; continue; } } if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_hdw_cmd_usbstream(0)"); pvr2_hdw_cmd_usbstream(hdw,0); } if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " decoder disable"); if (hdw->decoder_ctrl) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -