d1bafba615e4bf5e86b2bc806d4e51214395c9b5
[pandora-kernel.git] / drivers / media / dvb / firesat / cmp.c
1 /*
2  * FireDTV driver (formerly known as FireSAT)
3  *
4  * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
5  * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
6  *
7  *      This program is free software; you can redistribute it and/or
8  *      modify it under the terms of the GNU General Public License as
9  *      published by the Free Software Foundation; either version 2 of
10  *      the License, or (at your option) any later version.
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/mutex.h>
15 #include <linux/types.h>
16
17 #include <hosts.h>
18 #include <ieee1394.h>
19 #include <ieee1394_core.h>
20 #include <ieee1394_transactions.h>
21 #include <nodemgr.h>
22
23 #include "avc_api.h"
24 #include "cmp.h"
25 #include "firesat.h"
26
27 typedef struct _OPCR
28 {
29         __u8 PTPConnCount    : 6 ; // Point to point connect. counter
30         __u8 BrConnCount     : 1 ; // Broadcast connection counter
31         __u8 OnLine          : 1 ; // On Line
32
33         __u8 ChNr            : 6 ; // Channel number
34         __u8 Res             : 2 ; // Reserved
35
36         __u8 PayloadHi       : 2 ; // Payoad high bits
37         __u8 OvhdID          : 4 ; // Overhead ID
38         __u8 DataRate        : 2 ; // Data Rate
39
40         __u8 PayloadLo           ; // Payoad low byte
41 } OPCR ;
42
43 #define FIRESAT_SPEED IEEE1394_SPEED_400
44
45 static int cmp_read(struct firesat *firesat, void *buf, u64 addr, size_t len)
46 {
47         int ret;
48
49         if (mutex_lock_interruptible(&firesat->avc_mutex))
50                 return -EINTR;
51
52         ret = hpsb_read(firesat->host, firesat->nodeentry->nodeid,
53                         firesat->nodeentry->generation, addr, buf, len);
54
55         mutex_unlock(&firesat->avc_mutex);
56         return ret;
57 }
58
59 static int cmp_lock(struct firesat *firesat, quadlet_t *data, u64 addr,
60                 quadlet_t arg, int ext_tcode)
61 {
62         int ret;
63
64         if (mutex_lock_interruptible(&firesat->avc_mutex))
65                 return -EINTR;
66
67         ret = hpsb_lock(firesat->host, firesat->nodeentry->nodeid,
68                         firesat->nodeentry->generation,
69                         addr, ext_tcode, data, arg);
70
71         mutex_unlock(&firesat->avc_mutex);
72         return ret;
73 }
74
75 //try establishing a point-to-point connection (may be interrupted by a busreset
76 int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, int iso_channel) {
77         unsigned int BWU; //bandwidth to allocate
78
79         quadlet_t old_oPCR,test_oPCR = 0x0;
80         u64 oPCR_address=0xfffff0000904ull+(output_plug << 2);
81         int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4);
82
83 /*      printk(KERN_INFO "%s: nodeid = %d\n",__func__,firesat->nodeentry->nodeid); */
84
85         if (result < 0) {
86                 printk("%s: cannot read oPCR\n", __func__);
87                 return result;
88         } else {
89 /*              printk(KERN_INFO "%s: oPCR = %08x\n",__func__,test_oPCR); */
90                 do {
91                         OPCR *hilf= (OPCR*) &test_oPCR;
92
93                         if (!hilf->OnLine) {
94                                 printk("%s: Output offline; oPCR: %08x\n", __func__, test_oPCR);
95                                 return -EBUSY;
96                         } else {
97                                 quadlet_t new_oPCR;
98
99                                 old_oPCR=test_oPCR;
100                                 if (hilf->PTPConnCount) {
101                                         if (hilf->ChNr != iso_channel) {
102                                                 printk("%s: Output plug has already connection on channel %u; cannot change it to channel %u\n",__func__,hilf->ChNr,iso_channel);
103                                                 return -EBUSY;
104                                         } else
105                                                 printk(KERN_INFO "%s: Overlaying existing connection; connection counter was: %u\n",__func__, hilf->PTPConnCount);
106                                         BWU=0; //we allocate no bandwidth (is this necessary?)
107                                 } else {
108                                         hilf->ChNr=iso_channel;
109                                         hilf->DataRate=FIRESAT_SPEED;
110
111                                         hilf->OvhdID=0;      //FIXME: that is for worst case -> optimize
112                                         BWU=hilf->OvhdID?hilf->OvhdID*32:512;
113                                         BWU += (hilf->PayloadLo + (hilf->PayloadHi << 8) +3) * (2 << (3-hilf->DataRate));
114 /*                                      if (allocate_1394_resources(iso_channel,BWU))
115                                         {
116                                                 cout << "Allocation of resources failed\n";
117                                                 return -2;
118                                         }*/
119                                 }
120
121                                 hilf->PTPConnCount++;
122                                 new_oPCR=test_oPCR;
123 /*                              printk(KERN_INFO "%s: trying compare_swap...\n",__func__); */
124 /*                              printk(KERN_INFO "%s: oPCR_old: %08x, oPCR_new: %08x\n",__func__, old_oPCR, new_oPCR); */
125                                 result=cmp_lock(firesat, &test_oPCR, oPCR_address, old_oPCR, 2);
126
127                                 if (result < 0) {
128                                         printk("%s: cannot compare_swap oPCR\n",__func__);
129                                         return result;
130                                 }
131                                 if ((old_oPCR != test_oPCR) && (!((OPCR*) &old_oPCR)->PTPConnCount))
132                                 {
133                                         printk("%s: change of oPCR failed -> freeing resources\n",__func__);
134 //                                      hilf= (OPCR*) &new_oPCR;
135 //                                      unsigned int BWU=hilf->OvhdID?hilf->OvhdID*32:512;
136 //                                      BWU += (hilf->Payload+3) * (2 << (3-hilf->DataRate));
137 /*                                      if (deallocate_1394_resources(iso_channel,BWU))
138                                         {
139
140                                                 cout << "Deallocation of resources failed\n";
141                                                 return -3;
142                                         }*/
143                                 }
144                         }
145                 }
146                 while (old_oPCR != test_oPCR);
147         }
148         return 0;
149 }
150
151 //try breaking a point-to-point connection (may be interrupted by a busreset
152 int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug,int iso_channel) {
153         quadlet_t old_oPCR,test_oPCR;
154
155         u64 oPCR_address=0xfffff0000904ull+(output_plug << 2);
156         int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4);
157
158 /*      printk(KERN_INFO "%s\n",__func__); */
159
160         if (result < 0) {
161                 printk("%s: cannot read oPCR\n", __func__);
162                 return result;
163         } else {
164                 do {
165                         OPCR *hilf= (OPCR*) &test_oPCR;
166
167                         if (!hilf->OnLine || !hilf->PTPConnCount || hilf->ChNr != iso_channel) {
168                                 printk("%s: Output plug does not have PtP-connection on that channel; oPCR: %08x\n", __func__, test_oPCR);
169                                 return -EINVAL;
170                         } else {
171                                 quadlet_t new_oPCR;
172                                 old_oPCR=test_oPCR;
173                                 hilf->PTPConnCount--;
174                                 new_oPCR=test_oPCR;
175
176 //                              printk(KERN_INFO "%s: trying compare_swap...\n", __func__);
177                                 result=cmp_lock(firesat, &test_oPCR, oPCR_address, old_oPCR, 2);
178                                 if (result < 0) {
179                                         printk("%s: cannot compare_swap oPCR\n",__func__);
180                                         return result;
181                                 }
182                         }
183
184                 } while (old_oPCR != test_oPCR);
185
186 /*              hilf = (OPCR*) &old_oPCR;
187                 if (hilf->PTPConnCount == 1) { // if we were the last owner of this connection
188                         cout << "deallocating 1394 resources\n";
189                         unsigned int BWU=hilf->OvhdID?hilf->OvhdID*32:512;
190                         BWU += (hilf->PayloadLo + (hilf->PayloadHi << 8) +3) * (2 << (3-hilf->DataRate));
191                         if (deallocate_1394_resources(iso_channel,BWU))
192                         {
193                                 cout << "Deallocation of resources failed\n";
194                                 return -3;
195                         }
196                 }*/
197     }
198         return 0;
199 }