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