/* * tv.c control WatchMate, tuner board for PC * Copyright(c)1994 by Hiroaki Sengoku * Version 0.00 May 22, 1994 by sengoku@sdl.hitachi.co.jp */ #define TV_C 0x269 #define TV_D 0x26a #ifndef TV_CONFIG #define TV_CONFIG "/usr/local/lib/tv.config" #endif #include #include #ifdef BSD #include #include #include #else #include #endif #define ON 1 #define OFF 0 #define true 1 #define false 0 #define VAL_MAX 63 /* max value of volume, bright, ... */ int volume = 32; int volume_timer = 0; int mute_flag = false; #define NChTV 62 /* チャンネル数 */ #define NChCable 125 typedef enum { ModeTV, ModeCable, ModeLinear, ModeVideo, NMode } Mode; char *channel_name[NMode] = { " ", "c", " ", "v" }; int channel_max[NMode] = { NChTV, NChCable, 0x8000, 1 }; int channel[NMode] = { 1, 29, 0x0496, 1 }; Mode channel_mode = ModeTV; int channel_timer = 0; int direct_channel = 0; int direct_timer = 0; #define FineCenter 32 short tvch_fine[NChTV+1]; long tvch_table[NChTV+1] = { NChTV, 0x04960, 0x049c0, 0x04a20, 0x08e60, 0x08ec0, 0x08f20, /* 1 - 6 */ 0x08f80, 0x08fc0, 0x09020, 0x09080, 0x090e0, 0x09140, /* 7 - 12 */ 0x0a120, 0x0a180, 0x0a1e0, 0x0a240, 0x0a2a0, 0x0a300, /* 13 - 18 */ 0x0a360, 0x0a3c0, 0x0a420, 0x0a480, 0x0a4e0, 0x0a540, /* 19 - 24 */ 0x0a5a0, 0x0a600, 0x0a660, 0x0a6c0, 0x0a720, 0x0a780, /* 25 - 30 */ 0x0a7e0, 0x0a840, 0x0a8a0, 0x0a900, 0x0a960, 0x0a9c0, /* 31 - 36 */ 0x0aa20, 0x0aa80, 0x0aae0, 0x0ab40, 0x0aba0, 0x0ac00, /* 37 - 42 */ 0x0ac60, 0x0acc0, 0x0ad20, 0x0ad80, 0x0ade0, 0x0ae40, /* 43 - 48 */ 0x0aea0, 0x0af00, 0x0af60, 0x0afc0, 0x0b020, 0x0b080, /* 49 - 54 */ 0x0b0e0, 0x0b140, 0x0b1a0, 0x0b200, 0x0b260, 0x0b2c0, /* 55 - 60 */ 0x0b320, 0x0b380 }; /* 61 - 62 */ short cvch_fine[NChCable+1]; long cvch_table[NChCable+1] = { NChCable, 0x045f0, 0x04650, 0x046b0, 0x04710, 0x047b0, 0x04810, /* 1 - 6 */ 0x08dd0, 0x08e30, 0x08e90, 0x08ef0, 0x08f50, 0x08fb0, /* 7 - 12 */ 0x04a80, 0x04ae0, 0x04b40, 0x04ba0, 0x04c00, 0x04c60, /* 13 - 18 */ 0x04cc0, 0x04d20, 0x04d80, 0x08e00, 0x091a0, 0x09200, /* 19 - 24 */ 0x09260, 0x092c0, 0x09320, 0x09380, 0x093e0, 0x09440, /* 25 - 30 */ 0x094a0, 0x09500, 0x09560, 0x095c0, 0x09620, 0x09680, /* 31 - 36 */ 0x096e0, 0x09740, 0x097a0, 0x09800, 0x09860, 0x098c0, /* 37 - 42 */ 0x09920, 0x09980, 0x099e0, 0x09a40, 0x09aa0, 0x09b00, /* 43 - 48 */ 0x09b60, 0x09bc0, 0x09c20, 0x09c80, 0x09ce0, 0x09d40, /* 49 - 54 */ 0x09da0, 0x09e00, 0x09e60, 0x09ec0, 0x09f20, 0x09f80, /* 55 - 60 */ 0x09fe0, 0x0a040, 0x0a0a0, 0x11df0, 0x11e50, 0x11eb0, /* 61 - 66 */ 0x11f10, 0x11f70, 0x11fd0, 0x12030, 0x12090, 0x120f0, /* 67 - 72 */ 0x12150, 0x121b0, 0x12210, 0x12270, 0x122d0, 0x12330, /* 73 - 78 */ 0x12390, 0x123f0, 0x12450, 0x124b0, 0x12510, 0x12570, /* 79 - 84 */ 0x125d0, 0x12630, 0x12690, 0x126f0, 0x12750, 0x127b0, /* 85 - 90 */ 0x12810, 0x12870, 0x128d0, 0x12930, 0x12990, 0x129f0, /* 91 - 96 */ 0x12a50, 0x12ab0, 0x12b10, 0x12b70, 0x12bd0, 0x12c30, /* 97 - 102 */ 0x12c90, 0x12cf0, 0x12d50, 0x12db0, 0x12e10, 0x12e70, /* 103 - 108 */ 0x12ed0, 0x12f30, 0x12f90, 0x12ff0, 0x13050, 0x130b0, /* 109 - 114 */ 0x13110, 0x13170, 0x131d0, 0x13230, 0x13290, 0x132f0, /* 115 - 120 */ 0x13350, 0x133b0, 0x13410, 0x13470, 0x134d0 }; /* 121 - 125 */ short *channel_fine[NMode] = { tvch_fine, cvch_fine }; long *channel_table[NMode] = { tvch_table, cvch_table }; typedef enum { AdjFine, AdjBright, AdjCont, AdjSharp, AdjColor, AdjTint, NAdj } Adjust; char *adjust_name[NAdj] = { "FINETUNE","BRIGHT ","C0NTRAST","SHARP ","C0L0R ","TINT " }; int adjust_value[NAdj] = { 0, 25, 50, 45, 36, 45 }; int adjust_setting[NAdj] = { 0x000, 0x800, 0x200, 0x300, 0x400, 0x500 }; Adjust adjust_mode = 0; int clock_flag = false; extern int errno; static void inline port_out(unsigned char value, unsigned short port) { __asm__ volatile ("outb %b0,%1" ::"a" (value),"d" (port)); } static unsigned char inline port_in(unsigned short port) { unsigned char value; __asm__ volatile ("inb %1,%0" :"=a" (value):"d" (port)); return value; } void send_ctl(c) unsigned char c; { int i; port_out(c,TV_C); for( i=0; i < 20; i++ ) ; /* busy wait */ } void send_data(c) unsigned char c; { int i; port_out(c,TV_D); for( i=0; i < 20; i++ ) ; /* busy wait */ } /* 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 0 1 2 3 4 5 6 7 8 9 : < > − . , 1   A B C D E F G H I J K L M N ■ 2 P Q R S T U V W X Y Z 録 画 再 生 早 3 送 巻 戻 年 月 日 音 才 主 副 声 → ← ↑ ↓ ■ 4 ビ デ オ ス テ レ メ イ ン サ ブ カ ラ ー ィ ト 5 ? a b c d e f g h i j k l m n o 6 p q r s t u v w x y z ; × / ・ *1 7 *2 *3 *4 *5 *6 *7 *8 ♪ *9 *a *b *c *d *e *f *g *1 雪だるま *6 曇り (曇) *b 蛍光灯 *g 透明 *2 電話 *7 雨 (傘) *c 左スピーカ *3 女 *8 ? *d 右スピーカ *4 男 *9 カセット *e A 面再生 *5 晴れ (太陽) *a 電球 *f B 面再生 */ void send_char(val) int val; { int i; send_ctl(0x2); for( i=0; i < 8; i++ ) { if( val & 0x80 ) { send_data(0x1); send_data(0x3); } else { send_data(0x0); send_data(0x2); } val <<= 1; } send_data(0x6); send_data(0x2); } void draw_space(n) int n; { for( ; n > 0; n-- ) send_char(0x7f); } void clear() { draw_space(24*12); } void draw_char(c) char c; { switch(c) { case ' ': c = 0x7f; break; case ',': c = 0x0f; break; case '-': c = 0x0d; break; case '.': c = 0x0e; break; case '/': c = 0x6d; break; case ':': c = 0x0a; break; case ';': c = 0x6b; break; case '<': c = 0x0b; break; case '>': c = 0x0c; break; case '?': c = 0x50; break; case '@': c = 0x3f; break; case 'O': c = 0x00; break; /* O => 0 */ default: if( 'A' <= c && c <= 'Z' ) c += 0x11 - 'A'; else if( 'a' <= c && c <= 'z' ) c += 0x51 - 'a'; else if( '0' <= c && c <= '9' ) c -= '0'; } send_char(c); } void draw_str(str) char *str; { unsigned char c; while(c=*str++) draw_char(c); } void move_cur(x,y) int x, y; { send_char(0xfd); send_char(0x90|(y&0x0f)); send_char(0xfd); send_char(0xa0|(x&0x1f)); } #define BLACK 0 #define BLUE 1 #define GREEN 2 #define CYAN 3 #define RED 4 #define PURPLE 5 #define YELLOW 6 void set_color(col) int col; { send_char(0xfd); send_char(0x80|(col&0x0f)); } void clear_channel() { /* チャンネル表示を消す */ move_cur(20,0); draw_space(4); } void draw_mute() { /* ミュート表示 */ move_cur(9,5); set_color(RED); draw_str("MUTE"); } void clear_mute() { /* ミュート表示を消す */ move_cur(9,5); draw_space(4); } void draw_clock() { /* 時間を表示する */ time_t t; struct tm *tp; char str[10]; time(&t); tp = localtime(&t); move_cur(0,0); set_color(CYAN); sprintf(str,"%02d:%02d",tp->tm_hour,tp->tm_min); draw_str(str); } void clear_clock() { /* 時間表示を消す */ move_cur(0,0); draw_space(5); } void flush_volume() { /* ボリューム表示を消す */ adjust_mode = 0; move_cur(0,9); draw_space(24*2); } void tv_tune(f) long f; { int i; send_ctl(0x1); send_data(0x0); send_data(0x4); for( i=0; i < 18; i++ ) { if( f & 0x20000 ) { send_data(0x5); send_data(0x7); send_data(0x5); } else { send_data(0x4); send_data(0x6); send_data(0x4); } f <<= 1; } send_data(0x0); } void set_channel(table,fine_table,ch,fine) long *table; short *fine_table; int ch, fine; { long f; if( ch < 1 ) ch = 1; else if( ch > *table ) ch = *table; f = table[ch]; if( fine >= 0 ) fine_table[ch] = fine; else if( fine_table[ch] < 0 ) fine_table[ch] = FineCenter; adjust_value[AdjFine] = fine_table[ch]; f += fine_table[ch] - FineCenter; channel[ModeLinear] = f/16; tv_tune(f); } void tv_channel(mode,ch,fine) Mode mode; int ch, fine; { long f; char str[10]; flush_volume(); switch(mode) { case ModeTV: case ModeCable: set_channel(channel_table[mode],channel_fine[mode],ch,fine); break; case ModeLinear: f = ch*16; if( fine >= 0 ) f += fine - FineCenter; else adjust_value[AdjFine] = FineCenter; tv_tune(f); move_cur(20,0); set_color(RED); sprintf(str,"%4d",ch); draw_str(str); return; case ModeVideo: move_cur(20,0); set_color(RED); draw_str(" V"); channel_timer = 30; return; } move_cur(20,0); set_color(RED); sprintf(str,"%s%3d",channel_name[mode],ch); draw_str(str); channel_timer = 30; } void tv_channel_up(mode) Mode mode; { int ch, max; short *fine; ch = channel[mode]; max = channel_max[mode]; fine = channel_fine[mode]; if( ch < max ) { ch++; if( mode == ModeTV || mode == ModeCable ) { while( fine[ch] < 0 && ch < max ) ch++; if( fine[ch] < 0 ) ch = channel[mode]; } } if( ch != channel[mode] ) { channel[mode] = ch; tv_channel(mode,ch,-1); } } void tv_channel_down(mode) Mode mode; { int ch; short *fine; ch = channel[mode]; fine = channel_fine[mode]; if( ch > 1 ) { ch--; if( mode == ModeTV || mode == ModeCable ) { while( fine[ch] < 0 && ch > 1 ) ch--; if( fine[ch] < 0 ) ch = channel[mode]; } } if( ch != channel[mode] ) { channel[mode] = ch; tv_channel(mode,ch,-1); } } void set_value(mode,val) int mode; int val; { int i; val &= 0x3f; val |= mode; send_ctl(0x3); for( i=0; i < 12; i++ ) { if( val & 0x800 ) { send_data(0x1); send_data(0x3); } else { send_data(0x0); send_data(0x2); } val <<= 1; } send_data(0x6); send_data(0x2); } void draw_bar(val) int val; { int i; move_cur(0,10); set_color(CYAN); for( i=0; i < val/4; i++ ) draw_char('@'); for( ; i < VAL_MAX/4; i++ ) draw_char(' '); } void tv_volume(vol) int vol; { char str[10]; if( !mute_flag ) set_value(0x100,vol); move_cur(0,9); set_color(RED); draw_str("VOLUME "); move_cur(13,9); set_color(YELLOW); sprintf(str,"%2d",vol); draw_str(str); draw_bar(vol); volume_timer = 30; /* wait 3 sec before clear */ adjust_mode = 0; } void tv_adjust(mode,val) Adjust mode; int val; { char str[10]; if( mode == AdjFine ) { tv_channel(channel_mode,channel[channel_mode],val); } else if( mode == AdjCont ) { set_value(adjust_setting[mode],VAL_MAX - val); } else { set_value(adjust_setting[mode],val); } move_cur(0,9); set_color(RED); draw_str(adjust_name[mode]); draw_bar(val); if( mode == AdjFine ) val -= FineCenter; move_cur(12,9); set_color(YELLOW); sprintf(str,"%3d",val); draw_str(str); volume_timer = 30; /* wait 3 sec before clear */ } void tv_adjust_mode() { adjust_mode++; if( adjust_mode >= NAdj ) adjust_mode = AdjBright; tv_adjust(adjust_mode,adjust_value[adjust_mode]); } void tv_switch(flag) int flag; { send_ctl(0x4); if(flag) { if( channel_mode == ModeVideo ) { send_data(0xd); } else { send_data(0xf); /* turn on */ } } else { send_data(0x0); /* turn off */ } } void raw_mode(flag) int flag; { #ifdef BSD static struct termios save; struct termios t; #else static struct termio save; struct termio t; #endif if( flag ) { /* set term to raw mode */ #ifdef BSD ioctl(2,TIOCGETA,&t); #else ioctl(2,TCGETA,&t); #endif save = t; t.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); t.c_cc[VMIN] = 0; t.c_cc[VTIME] = 1; /* 0.1 sec to expire */ } else { /* restore */ t = save; } #ifdef BSD ioctl(2,TIOCSETAW,&t); #else ioctl(2,TCSETAW,&t); #endif } void tick() { /* 0.1 秒に一度 */ static int count = 0; count++; if( volume_timer && --volume_timer <= 0 ) flush_volume(); if( channel_timer && --channel_timer <=0 ) clear_channel(); if( direct_timer && --direct_timer <= 0 && direct_channel ) { channel[channel_mode] = direct_channel; direct_channel = 0; tv_channel(channel_mode,channel[channel_mode],-1); } if( clock_flag && count%100 == 0 ) draw_clock(); } #define KEY_UP (0x100 | 'A') #define KEY_DOWN (0x100 | 'B') #define KEY_RIGHT (0x100 | 'C') #define KEY_LEFT (0x100 | 'D') #define KEY_F5 0x205 #define KEY_F6 0x207 #define KEY_F7 0x208 #define KEY_F8 0x209 #define KEY_F9 0x20a #define KEY_F10 0x20b #define KEY_F11 0x20d #define KEY_F12 0x20e #define KEY_PGUP 0x215 #define KEY_PGDN 0x216 int read_key() { unsigned char c; int prev = -1; int done = false; int ret; while( !done ) { while( read(2,&c,sizeof(c)) <= 0 ) { tick(); if( prev >= 0 ) { ret = prev; prev = -2; done = true; break; } } switch(prev) { case 0x1b: prev = c; break; case '[': if( 'A' <= c && c <= 'D' ) { ret = (0x100 | c); done = true; #ifdef BSD } else if( 'Q' <= c && c <= 'V' ) { if( c == 'Q' ) c--; ret = (0x200 | c - 'K'); done = true; #endif } else if( '5' <= c && c <= '6' ) { prev = (0x210 | c - '0'); } else prev = c; break; case '1': if( '0' <= c && c <= '9' ) prev = (0x200 | c - '0'); break; case '2': if( '0' <= c && c <= '9' ) prev = (0x200 | c - '0') + 10; break; default: if( (prev & 0xff00) == 0x200 && c == '~' ) { ret = prev; done = true; } else if( c != 0x1b ) { ret = c; done = true; } else { prev = c; } } } return ret; } void tv_initial() { static int str[] = { 0xff, 0xe0, 0xfd, 0xc0, 0xfd, 0xe9, 0xfd, 0xf0, 0xff, 0xc4, 0xff, 0x44, -1 }; int i; for( i=AdjBright; i < NAdj; i++ ) tv_adjust(i,adjust_value[i]); tv_volume(volume); tv_channel(channel_mode,channel[channel_mode],-1); clear(); send_ctl(0x5); send_data(0x2e); send_ctl(0x6); send_data(0x2b); for( i=0; str[i] >= 0; i++ ) send_char(str[i]); } void read_config(fp) FILE *fp; { char str[256]; int r, i; do { r = fscanf(fp,"%s",str); if( !strcmp(str,"Channel:") ) { for( i=0; i < NMode && r > 0 ; i++ ) r = fscanf(fp,"%d",&channel[i]); } else if( !strcmp(str,"Channel-Mode:") ) { r = fscanf(fp,"%d",&channel_mode); } else if( !strcmp(str,"Adjust:") ) { for( i=1; i < NAdj && r > 0; i++ ) r = fscanf(fp,"%d",&adjust_value[i]); } else if( !strcmp(str,"Volume:") ) { r = fscanf(fp,"%d",&volume); } else if( !strcmp(str,"TV-Fine-Tune:") ) { for( i=0; i < *tvch_table && r > 0; i++ ) { if( (r=fscanf(fp,"%d",&tvch_fine[i])) > 0 ) tvch_fine[i] += FineCenter; } } else if( !strcmp(str,"Cable-Fine-Tune:") ) { for( i=0; i < *cvch_table && r > 0; i++ ) { if( (r=fscanf(fp,"%d",&cvch_fine[i])) > 0 ) cvch_fine[i] += FineCenter; } } else r = -1; } while( r > 0 ); } void write_config(fp) FILE *fp; { int i; fprintf(fp,"Channel:"); for( i=0; i < NMode; i++ ) fprintf(fp," %d",channel[i]); fprintf(fp,"\n"); fprintf(fp,"Channel-Mode: %d\n",channel_mode); fprintf(fp,"Adjust:"); for( i=1; i < NAdj; i++ ) fprintf(fp," %d",adjust_value[i]); fprintf(fp,"\n"); fprintf(fp,"Volume: %d\n",volume); fprintf(fp,"TV-Fine-Tune:"); for( i=0; i < *tvch_table; i++ ) fprintf(fp," %d",tvch_fine[i]-FineCenter); fprintf(fp,"\n"); fprintf(fp,"Cable-Fine-Tune:"); for( i=0; i < *cvch_table; i++ ) fprintf(fp," %d",cvch_fine[i]-FineCenter); fprintf(fp,"\n"); } main(int argc, char *argv[]) { FILE *fp; int c; int i; int done = false; for( i=0; i < NChTV; i++ ) tvch_fine[i] = FineCenter; for( i=0; i < NChCable; i++ ) cvch_fine[i] = FineCenter; if( fp=fopen(TV_CONFIG,"r") ) { read_config(fp); fclose(fp); } #ifdef BSD if( open("/dev/io",O_RDONLY) < 0 ) #else if( ioperm(TV_C,1,1) || ioperm(TV_D,1,1) ) #endif { fprintf(stderr,"can't get I/O permissions \n"); exit(-1); } raw_mode(ON); tv_initial(); tv_switch(ON); while( !done ) { c = read_key(); if( '0' <= c && c <= '9' ) { direct_channel = direct_channel*10 + (c - '0'); direct_timer = 7; continue; } direct_channel = direct_timer = 0; switch(c) { case 'x': case 'X': fp = fopen(TV_CONFIG,"w"); write_config(fp); fclose(fp); case 'D'-'@': /* control-D */ case 0x1b: /* esc */ case 'q': case 'Q': mute_flag = false; tv_volume(0); /* mute */ tv_switch(OFF); done = true; break; case '-': case KEY_PGDN: tv_channel_down(channel_mode); break; case '+': case '=': case KEY_PGUP: tv_channel_up(channel_mode); break; case 0x7f: if( channel_mode == ModeTV || channel_mode == ModeCable ) { channel_fine[channel_mode][channel[channel_mode]] = -FineCenter; } break; case 'c': case 'C': channel_mode = ModeCable; tv_switch(ON); tv_channel(channel_mode,channel[channel_mode],-1); break; case 't': case 'T': channel_mode = ModeTV; tv_switch(ON); tv_channel(channel_mode,channel[channel_mode],-1); break; case 'v': case 'V': channel_mode = ModeVideo; tv_switch(ON); tv_channel(channel_mode,channel[channel_mode],-1); break; case 'l': channel_mode = ModeLinear; tv_switch(ON); tv_channel(channel_mode,channel[channel_mode],-1); break; case KEY_DOWN: if( volume > 0 ) volume--; tv_volume(volume); break; case KEY_UP: clear_mute(); mute_flag = false; if( volume < VAL_MAX ) volume++; tv_volume(volume); break; case 'm': case 'M': if( mute_flag ) { mute_flag = false; tv_volume(volume); clear_mute(); } else { set_value(0x100,0); mute_flag = true; draw_mute(); } break; case KEY_RIGHT: if( adjust_value[adjust_mode] < VAL_MAX ) adjust_value[adjust_mode]++; tv_adjust(adjust_mode,adjust_value[adjust_mode]); break; case KEY_LEFT: if( adjust_value[adjust_mode] > 0 ) adjust_value[adjust_mode]--; tv_adjust(adjust_mode,adjust_value[adjust_mode]); break; case KEY_F6: clock_flag = false; clear(); break; case KEY_F7: if( clock_flag ) { clock_flag = false; clear_clock(); } else { clock_flag = true; draw_clock(); } break; case KEY_F8: tv_adjust_mode(); break; default: printf(" char=0x%x\n",c); } } raw_mode(OFF); } /* * For Gnu Emacs. * Local Variables: * tab-width: 4 * End: */