blob: 9428d8b2642c9b7a103eca1f8b8156d3e66e3935 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Ingo Molnar3593cab2006-02-07 06:49:14 -020021#include <linux/mutex.h>
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <asm/io.h>
Michael Krufky2938d782006-05-23 18:39:29 -030024#include "oss/aci.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include "miropcm20-rds-core.h"
26
27#define DEBUG 0
28
Ingo Molnar3593cab2006-02-07 06:49:14 -020029static struct mutex aci_rds_mutex;
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
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
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030036#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
38
39#if DEBUG
40static void print_matrix(char array[], unsigned int length)
41{
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030042 int i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -030044 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070054}
55#endif /* DEBUG */
56
57static 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
70static 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/* don't use any ..._nowait() function if you are not sure what you do... */
94
95static 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
104static 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
113static int rds_write(unsigned char cmd)
114{
115 unsigned char sendbuffer[8];
116 int i;
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -0300117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 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
128static int rds_readcycle_nowait(void)
129{
130 rds_rawwrite_nowait(0);
131 return rds_waitread();
132}
133
134static int rds_readcycle(void)
135{
136 if (rds_rawwrite(0) < 0)
137 return -1;
138 return rds_waitread();
139}
140
141static 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;
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -0300154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 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
167static 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
181int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
182{
183 int ret;
184
Ingo Molnar3593cab2006-02-07 06:49:14 -0200185 if (mutex_lock_interruptible(&aci_rds_mutex))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 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
Ingo Molnar3593cab2006-02-07 06:49:14 -0200196 mutex_unlock(&aci_rds_mutex);
Mauro Carvalho Chehab4286c6f2006-04-08 16:06:16 -0300197
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 return ret;
199}
200EXPORT_SYMBOL(aci_rds_cmd);
201
202int __init attach_aci_rds(void)
203{
Ingo Molnar3593cab2006-02-07 06:49:14 -0200204 mutex_init(&aci_rds_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 return 0;
206}
207
208void __exit unload_aci_rds(void)
209{
210}
211MODULE_LICENSE("GPL");