📄 tveeprom.c
字号:
break; } len = eeprom_data[i] & 0x07; ++i; } else { tveeprom_warn("Encountered bad packet header [%02x]. " "Corrupt or not a Hauppauge eeprom.\n", eeprom_data[i]); return; } if (debug) { tveeprom_info("Tag [%02x] + %d bytes:", eeprom_data[i], len - 1); for(j = 1; j < len; j++) { printk(" %02x", eeprom_data[i + j]); } printk("\n"); } /* process by tag */ tag = eeprom_data[i]; switch (tag) { case 0x00: /* tag: 'Comprehensive' */ tuner1 = eeprom_data[i+6]; t_format1 = eeprom_data[i+5]; tvee->has_radio = eeprom_data[i+len-1]; /* old style tag, don't know how to detect IR presence, mark as unknown. */ tvee->has_ir = 2; tvee->model = eeprom_data[i+8] + (eeprom_data[i+9] << 8); tvee->revision = eeprom_data[i+10] + (eeprom_data[i+11] << 8) + (eeprom_data[i+12] << 16); break; case 0x01: /* tag: 'SerialID' */ tvee->serial_number = eeprom_data[i+6] + (eeprom_data[i+7] << 8) + (eeprom_data[i+8] << 16); break; case 0x02: /* tag 'AudioInfo' Note mask with 0x7F, high bit used on some older models to indicate 4052 mux was removed in favor of using MSP inputs directly. */ audioic = eeprom_data[i+2] & 0x7f; if (audioic < sizeof(audioIC)/sizeof(*audioIC)) tvee->audio_processor = audioIC[audioic].id; else tvee->audio_processor = AUDIO_CHIP_UNKNOWN; break; /* case 0x03: tag 'EEInfo' */ case 0x04: /* tag 'SerialID2' */ tvee->serial_number = eeprom_data[i+5] + (eeprom_data[i+6] << 8) + (eeprom_data[i+7] << 16); if ( (eeprom_data[i + 8] && 0xf0) && (tvee->serial_number < 0xffffff) ) { tvee->MAC_address[0] = 0x00; tvee->MAC_address[1] = 0x0D; tvee->MAC_address[2] = 0xFE; tvee->MAC_address[3] = eeprom_data[i + 7]; tvee->MAC_address[4] = eeprom_data[i + 6]; tvee->MAC_address[5] = eeprom_data[i + 5]; tvee->has_MAC_address = 1; } break; case 0x05: /* tag 'Audio2' Note mask with 0x7F, high bit used on some older models to indicate 4052 mux was removed in favor of using MSP inputs directly. */ audioic = eeprom_data[i+1] & 0x7f; if (audioic < sizeof(audioIC)/sizeof(*audioIC)) tvee->audio_processor = audioIC[audioic].id; else tvee->audio_processor = AUDIO_CHIP_UNKNOWN; break; case 0x06: /* tag 'ModelRev' */ tvee->model = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8) + (eeprom_data[i + 3] << 16) + (eeprom_data[i + 4] << 24); tvee->revision = eeprom_data[i +5 ] + (eeprom_data[i + 6] << 8) + (eeprom_data[i + 7] << 16); break; case 0x07: /* tag 'Details': according to Hauppauge not interesting on any PCI-era or later boards. */ break; /* there is no tag 0x08 defined */ case 0x09: /* tag 'Video' */ tvee->decoder_processor = eeprom_data[i + 1]; break; case 0x0a: /* tag 'Tuner' */ if (beenhere == 0) { tuner1 = eeprom_data[i+2]; t_format1 = eeprom_data[i+1]; beenhere = 1; } else { /* a second (radio) tuner may be present */ tuner2 = eeprom_data[i+2]; t_format2 = eeprom_data[i+1]; if (t_format2 == 0) { /* not a TV tuner? */ tvee->has_radio = 1; /* must be radio */ } } break; case 0x0b: /* tag 'Inputs': according to Hauppauge this is specific to each driver family, so no good assumptions can be made. */ break; /* case 0x0c: tag 'Balun' */ /* case 0x0d: tag 'Teletext' */ case 0x0e: /* tag: 'Radio' */ tvee->has_radio = eeprom_data[i+1]; break; case 0x0f: /* tag 'IRInfo' */ tvee->has_ir = eeprom_data[i+1]; break; /* case 0x10: tag 'VBIInfo' */ /* case 0x11: tag 'QCInfo' */ /* case 0x12: tag 'InfoBits' */ default: tveeprom_dbg("Not sure what to do with tag [%02x]\n", tag); /* dump the rest of the packet? */ } } if (!done) { tveeprom_warn("Ran out of data!\n"); return; } if (tvee->revision != 0) { tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); tvee->rev_str[3] = 32 + ( tvee->revision & 0x3f); tvee->rev_str[4] = 0; } if (hasRadioTuner(tuner1) && !tvee->has_radio) { tveeprom_info("The eeprom says no radio is present, but the tuner type\n"); tveeprom_info("indicates otherwise. I will assume that radio is present.\n"); tvee->has_radio = 1; } if (tuner1 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { tvee->tuner_type = hauppauge_tuner[tuner1].id; t_name1 = hauppauge_tuner[tuner1].name; } else { t_name1 = "unknown"; } if (tuner2 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { tvee->tuner2_type = hauppauge_tuner[tuner2].id; t_name2 = hauppauge_tuner[tuner2].name; } else { t_name2 = "unknown"; } tvee->tuner_hauppauge_model = tuner1; tvee->tuner2_hauppauge_model = tuner2; tvee->tuner_formats = 0; tvee->tuner2_formats = 0; for (i = j = 0; i < 8; i++) { if (t_format1 & (1 << i)) { tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; } if (t_format2 & (1 << i)) { tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; } } tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", tvee->model, tvee->rev_str, tvee->serial_number); if (tvee->has_MAC_address == 1) { tveeprom_info("MAC address is %02X-%02X-%02X-%02X-%02X-%02X\n", tvee->MAC_address[0], tvee->MAC_address[1], tvee->MAC_address[2], tvee->MAC_address[3], tvee->MAC_address[4], tvee->MAC_address[5]); } tveeprom_info("tuner model is %s (idx %d, type %d)\n", t_name1, tuner1, tvee->tuner_type); tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5], t_fmt_name1[6], t_fmt_name1[7], t_format1); if (tuner2) { tveeprom_info("second tuner model is %s (idx %d, type %d)\n", t_name2, tuner2, tvee->tuner2_type); } if (t_format2) { tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5], t_fmt_name2[6], t_fmt_name2[7], t_format2); } if (audioic<0) { tveeprom_info("audio processor is unknown (no idx)\n"); tvee->audio_processor=AUDIO_CHIP_UNKNOWN; } else { if (audioic < sizeof(audioIC)/sizeof(*audioIC)) tveeprom_info("audio processor is %s (idx %d)\n", audioIC[audioic].name,audioic); else tveeprom_info("audio processor is unknown (idx %d)\n", audioic); } if (tvee->decoder_processor) { tveeprom_info("decoder processor is %s (idx %d)\n", STRM(decoderIC, tvee->decoder_processor), tvee->decoder_processor); } if (tvee->has_ir == 2) tveeprom_info("has %sradio\n", tvee->has_radio ? "" : "no "); else tveeprom_info("has %sradio, has %sIR remote\n", tvee->has_radio ? "" : "no ", tvee->has_ir ? "" : "no ");}EXPORT_SYMBOL(tveeprom_hauppauge_analog);/* ----------------------------------------------------------------------- *//* generic helper functions */int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len){ unsigned char buf; int err; buf = 0; if (1 != (err = i2c_master_send(c, &buf, 1))) { tveeprom_info("Huh, no eeprom present (err=%d)?\n", err); return -1; } if (len != (err = i2c_master_recv(c, eedata, len))) { tveeprom_warn("i2c eeprom read error (err=%d)\n", err); return -1; } if (debug) { int i; tveeprom_info("full 256-byte eeprom dump:\n"); for (i = 0; i < len; i++) { if (0 == (i % 16)) tveeprom_info("%02x:", i); printk(" %02x", eedata[i]); if (15 == (i % 16)) printk("\n"); } } return 0;}EXPORT_SYMBOL(tveeprom_read);/* ----------------------------------------------------------------------- *//* needed for ivtv.sf.net at the moment. Should go away in the long *//* run, just call the exported tveeprom_* directly, there is no point in *//* using the indirect way via i2c_driver->command() */static unsigned short normal_i2c[] = { 0xa0 >> 1, I2C_CLIENT_END,};I2C_CLIENT_INSMOD;static struct i2c_driver i2c_driver_tveeprom;static inttveeprom_command(struct i2c_client *client, unsigned int cmd, void *arg){ struct tveeprom eeprom; u32 *eeprom_props = arg; u8 *buf; switch (cmd) { case 0: buf = kmalloc(256,GFP_KERNEL); memset(buf,0,256); tveeprom_read(client,buf,256); tveeprom_hauppauge_analog(client, &eeprom,buf); kfree(buf); eeprom_props[0] = eeprom.tuner_type; eeprom_props[1] = eeprom.tuner_formats; eeprom_props[2] = eeprom.model; eeprom_props[3] = eeprom.revision; eeprom_props[4] = eeprom.has_radio; break; default: return -EINVAL; } return 0;}static inttveeprom_detect_client(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *client; client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); if (NULL == client) return -ENOMEM; memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_tveeprom; client->flags = I2C_CLIENT_ALLOW_USE; snprintf(client->name, sizeof(client->name), "tveeprom"); i2c_attach_client(client); return 0;}static inttveeprom_attach_adapter (struct i2c_adapter *adapter){ if (adapter->id != I2C_HW_B_BT848) return 0; return i2c_probe(adapter, &addr_data, tveeprom_detect_client);}static inttveeprom_detach_client (struct i2c_client *client){ int err; err = i2c_detach_client(client); if (err < 0) return err; kfree(client); return 0;}static struct i2c_driver i2c_driver_tveeprom = { .owner = THIS_MODULE, .name = "tveeprom", .id = I2C_DRIVERID_TVEEPROM, .flags = I2C_DF_NOTIFY, .attach_adapter = tveeprom_attach_adapter, .detach_client = tveeprom_detach_client, .command = tveeprom_command,};static int __init tveeprom_init(void){ return i2c_add_driver(&i2c_driver_tveeprom);}static void __exit tveeprom_exit(void){ i2c_del_driver(&i2c_driver_tveeprom);}module_init(tveeprom_init);module_exit(tveeprom_exit);/* * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -