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
print_matrix(char array[],unsigned int length)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 
byte2trans(unsigned char byte,unsigned char sendbuffer[],int size)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 
rds_waitread(void)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 
rds_rawwrite_nowait(unsigned char byte)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 
rds_rawwrite(unsigned char byte)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 
rds_write(unsigned char cmd)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 
rds_readcycle_nowait(void)128 static int rds_readcycle_nowait(void)
129 {
130 	rds_rawwrite_nowait(0);
131 	return rds_waitread();
132 }
133 
rds_readcycle(void)134 static int rds_readcycle(void)
135 {
136 	if (rds_rawwrite(0) < 0)
137 		return -1;
138 	return rds_waitread();
139 }
140 
rds_read(unsigned char databuffer[],int datasize)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 
rds_ack(void)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 
aci_rds_cmd(unsigned char cmd,unsigned char databuffer[],int datasize)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 
attach_aci_rds(void)202 int __init attach_aci_rds(void)
203 {
204 	init_MUTEX(&aci_rds_sem);
205 	return 0;
206 }
207 
unload_aci_rds(void)208 void __exit unload_aci_rds(void)
209 {
210 }
211 MODULE_LICENSE("GPL");
212