File: /usr/src/linux/drivers/media/radio/miropcm20-rds-core.c

1     /*
2      *  Many thanks to Fred Seidel <seidel@metabox.de>, the
3      *  designer of the RDS decoder hardware. With his help
4      *  I was able to code this driver.
5      *  Thanks also to Norberto Pellicci, Dominic Mounteney
6      *  <DMounteney@pinnaclesys.com> and www.teleauskunft.de
7      *  for good hints on finding Fred. It was somewhat hard
8      *  to locate him here in Germany... [:
9      *
10      * Revision history:
11      *
12      *   2000-08-09  Robert Siemer <Robert.Siemer@gmx.de>
13      *        RDS support for MiroSound PCM20 radio
14      */
15     
16     #define _NO_VERSION_
17     
18     /* #include <linux/kernel.h> */
19     #include <linux/module.h>
20     #include <linux/init.h>
21     #include <linux/slab.h>
22     #include <asm/semaphore.h>
23     #include <asm/io.h>
24     #include "../../sound/aci.h"
25     #include "miropcm20-rds-core.h"
26     
27     #define DEBUG 0
28     
29     static struct semaphore aci_rds_sem;
30     
31     #define RDS_DATASHIFT          2   /* Bit 2 */
32     #define RDS_DATAMASK        (1 << RDS_DATASHIFT)
33     #define RDS_BUSYMASK        0x10   /* Bit 4 */
34     #define RDS_CLOCKMASK       0x08   /* Bit 3 */
35     
36     #define RDS_DATA(x)         (((x) >> RDS_DATASHIFT) & 1) 
37     
38     
39     #if DEBUG
40     static void print_matrix(char array[], unsigned int length)
41     {
42             int i, j;
43     
44             for (i=0; i<length; i++) {
45                     printk(KERN_DEBUG "aci-rds: ");
46                     for (j=7; j>=0; j--) {
47                             printk("%d", (array[i] >> j) & 0x1);
48                     }
49                     if (i%8 == 0)
50                             printk(" byte-border\n");
51                     else
52                             printk("\n");
53             }
54     }
55     #endif /* DEBUG */
56     
57     static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size)
58     {
59     	int i;
60     
61     	if (size != 8)
62     		return -1;
63     	for (i = 7; i >= 0; i--)
64     		sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
65     	sendbuffer[0] |= RDS_CLOCKMASK;
66     
67     	return 0;
68     }
69     
70     static int rds_waitread(void)
71     {
72     	unsigned char byte;
73     	int i=2000;
74     
75     	do {
76     		byte=inb(RDS_REGISTER);
77     		i--;
78     	}
79     	while ((byte & RDS_BUSYMASK) && i);
80     
81     	if (i) {
82     		#if DEBUG
83     		printk(KERN_DEBUG "rds_waitread()");
84     		print_matrix(&byte, 1);
85     		#endif
86     		return (byte);
87     	} else {
88     		printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n");
89     		return -1;
90     	}
91     }
92     
93     /* dont use any ..._nowait() function if you are not sure what you do... */
94     
95     static inline void rds_rawwrite_nowait(unsigned char byte)
96     {
97     	#if DEBUG
98     	printk(KERN_DEBUG "rds_rawwrite()");
99     	print_matrix(&byte, 1);
100     	#endif
101     	outb(byte, RDS_REGISTER);
102     }
103     
104     static int rds_rawwrite(unsigned char byte)
105     {
106     	if (rds_waitread() >= 0) {
107     		rds_rawwrite_nowait(byte);
108     		return 0;
109     	} else
110     		return -1;
111     }
112     
113     static int rds_write(unsigned char cmd)
114     {
115     	unsigned char sendbuffer[8];
116     	int i;
117     	
118     	if (byte2trans(cmd, sendbuffer, 8) != 0){
119     		return -1;
120     	} else {
121     		for (i=0; i<8; i++) {
122     			rds_rawwrite(sendbuffer[i]);
123     		}
124     	}
125     	return 0;
126     }
127     
128     static int rds_readcycle_nowait(void)
129     {
130     	rds_rawwrite_nowait(0);
131     	return rds_waitread();
132     }
133     
134     static int rds_readcycle(void)
135     {
136     	if (rds_rawwrite(0) < 0)
137     		return -1;
138     	return rds_waitread();
139     }
140     
141     static int rds_read(unsigned char databuffer[], int datasize)
142     {
143     	#define READSIZE (8*datasize)
144     
145     	int i,j;
146     
147     	if (datasize < 1)  /* nothing to read */
148     		return 0;
149     
150     	/* to be able to use rds_readcycle_nowait()
151     	   I have to waitread() here */
152     	if (rds_waitread() < 0)
153     		return -1;
154     	
155     	memset(databuffer, 0, datasize);
156     
157     	for (i=0; i< READSIZE; i++)
158     		if((j=rds_readcycle_nowait()) < 0) {
159     			return -1;
160     		} else {
161     			databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8)));
162     		}
163     
164     	return 0;
165     }
166     
167     static int rds_ack(void)
168     {
169     	int i=rds_readcycle();
170     
171     	if (i < 0)
172     		return -1;
173     	if (i & RDS_DATAMASK) {
174     		return 0;  /* ACK  */
175     	} else {
176     		printk(KERN_DEBUG "aci-rds: NACK\n");
177     		return 1;  /* NACK */
178     	}
179     }
180     
181     int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
182     {
183     	int ret;
184     
185     	if (down_interruptible(&aci_rds_sem))
186     		return -EINTR;
187     
188     	rds_write(cmd);
189     
190     	/* RDS_RESET doesn't need further processing */
191     	if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize)))
192     		ret = -1;
193     	else
194     		ret = 0;
195     
196     	up(&aci_rds_sem);
197     	
198     	return ret;
199     }
200     EXPORT_SYMBOL(aci_rds_cmd);
201     
202     int __init attach_aci_rds(void)
203     {
204     	init_MUTEX(&aci_rds_sem);
205     	return 0;
206     }
207     
208     void __exit unload_aci_rds(void)
209     {
210     }
211