Commit | Line | Data |
---|---|---|
1da177e4 LT |
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> | |
3593cab5 IM |
21 | #include <linux/mutex.h> |
22 | ||
1da177e4 | 23 | #include <asm/io.h> |
2938d780 | 24 | #include "oss/aci.h" |
1da177e4 LT |
25 | #include "miropcm20-rds-core.h" |
26 | ||
27 | #define DEBUG 0 | |
28 | ||
3593cab5 | 29 | static struct mutex aci_rds_mutex; |
1da177e4 LT |
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 | ||
4286c6f6 | 36 | #define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1) |
1da177e4 LT |
37 | |
38 | ||
39 | #if DEBUG | |
40 | static void print_matrix(char array[], unsigned int length) | |
41 | { | |
4286c6f6 MCC |
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 | } | |
1da177e4 LT |
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 | /* don't 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; | |
4286c6f6 | 117 | |
1da177e4 LT |
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; | |
4286c6f6 | 154 | |
1da177e4 LT |
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 | ||
3593cab5 | 185 | if (mutex_lock_interruptible(&aci_rds_mutex)) |
1da177e4 LT |
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 | ||
3593cab5 | 196 | mutex_unlock(&aci_rds_mutex); |
4286c6f6 | 197 | |
1da177e4 LT |
198 | return ret; |
199 | } | |
200 | EXPORT_SYMBOL(aci_rds_cmd); | |
201 | ||
202 | int __init attach_aci_rds(void) | |
203 | { | |
3593cab5 | 204 | mutex_init(&aci_rds_mutex); |
1da177e4 LT |
205 | return 0; |
206 | } | |
207 | ||
208 | void __exit unload_aci_rds(void) | |
209 | { | |
210 | } | |
211 | MODULE_LICENSE("GPL"); |