Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / usb / host / dwc_common_port / dwc_cc.c
1 /* =========================================================================
2  * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.c $
3  * $Revision: #4 $
4  * $Date: 2010/11/04 $
5  * $Change: 1621692 $
6  *
7  * Synopsys Portability Library Software and documentation
8  * (hereinafter, "Software") is an Unsupported proprietary work of
9  * Synopsys, Inc. unless otherwise expressly agreed to in writing
10  * between Synopsys and you.
11  *
12  * The Software IS NOT an item of Licensed Software or Licensed Product
13  * under any End User Software License Agreement or Agreement for
14  * Licensed Product with Synopsys or any supplement thereto. You are
15  * permitted to use and redistribute this Software in source and binary
16  * forms, with or without modification, provided that redistributions
17  * of source code must retain this notice. You may not view, use,
18  * disclose, copy or distribute this file or any information contained
19  * herein except pursuant to this license grant from Synopsys. If you
20  * do not agree with this notice, including the disclaimer below, then
21  * you are not authorized to use the Software.
22  *
23  * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
24  * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
27  * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
31  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
33  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
34  * DAMAGE.
35  * ========================================================================= */
36 #ifdef DWC_CCLIB
37
38 #include "dwc_cc.h"
39
40 typedef struct dwc_cc
41 {
42         uint32_t uid;
43         uint8_t chid[16];
44         uint8_t cdid[16];
45         uint8_t ck[16];
46         uint8_t *name;
47         uint8_t length;
48         DWC_CIRCLEQ_ENTRY(dwc_cc) list_entry;
49 } dwc_cc_t;
50
51 DWC_CIRCLEQ_HEAD(context_list, dwc_cc);
52
53 /** The main structure for CC management.  */
54 struct dwc_cc_if
55 {
56         dwc_mutex_t *mutex;
57         char *filename;
58
59         unsigned is_host:1;
60
61         dwc_notifier_t *notifier;
62
63         struct context_list list;
64 };
65
66 #ifdef DEBUG
67 static inline void dump_bytes(char *name, uint8_t *bytes, int len)
68 {
69         int i;
70         DWC_PRINTF("%s: ", name);
71         for (i=0; i<len; i++) {
72                 DWC_PRINTF("%02x ", bytes[i]);
73         }
74         DWC_PRINTF("\n");
75 }
76 #else
77 #define dump_bytes(x...)
78 #endif
79
80 static dwc_cc_t *alloc_cc(void *mem_ctx, uint8_t *name, uint32_t length)
81 {
82         dwc_cc_t *cc = dwc_alloc(mem_ctx, sizeof(dwc_cc_t));
83         if (!cc) {
84                 return NULL;
85         }
86         DWC_MEMSET(cc, 0, sizeof(dwc_cc_t));
87
88         if (name) {
89                 cc->length = length;
90                 cc->name = dwc_alloc(mem_ctx, length);
91                 if (!cc->name) {
92                         dwc_free(mem_ctx, cc);
93                         return NULL;
94                 }
95
96                 DWC_MEMCPY(cc->name, name, length);
97         }
98
99         return cc;
100 }
101
102 static void free_cc(void *mem_ctx, dwc_cc_t *cc)
103 {
104         if (cc->name) {
105                 dwc_free(mem_ctx, cc->name);
106         }
107         dwc_free(mem_ctx, cc);
108 }
109
110 static uint32_t next_uid(dwc_cc_if_t *cc_if)
111 {
112         uint32_t uid = 0;
113         dwc_cc_t *cc;
114         DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
115                 if (cc->uid > uid) {
116                         uid = cc->uid;
117                 }
118         }
119
120         if (uid == 0) {
121                 uid = 255;
122         }
123
124         return uid + 1;
125 }
126
127 static dwc_cc_t *cc_find(dwc_cc_if_t *cc_if, uint32_t uid)
128 {
129         dwc_cc_t *cc;
130         DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
131                 if (cc->uid == uid) {
132                         return cc;
133                 }
134         }
135         return NULL;
136 }
137
138 static unsigned int cc_data_size(dwc_cc_if_t *cc_if)
139 {
140         unsigned int size = 0;
141         dwc_cc_t *cc;
142         DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
143                 size += (48 + 1);
144                 if (cc->name) {
145                         size += cc->length;
146                 }
147         }
148         return size;
149 }
150
151 static uint32_t cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
152 {
153         uint32_t uid = 0;
154         dwc_cc_t *cc;
155
156         DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
157                 if (DWC_MEMCMP(cc->chid, chid, 16) == 0) {
158                         uid = cc->uid;
159                         break;
160                 }
161         }
162         return uid;
163 }
164 static uint32_t cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
165 {
166         uint32_t uid = 0;
167         dwc_cc_t *cc;
168
169         DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
170                 if (DWC_MEMCMP(cc->cdid, cdid, 16) == 0) {
171                         uid = cc->uid;
172                         break;
173                 }
174         }
175         return uid;
176 }
177
178 /* Internal cc_add */
179 static int32_t cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
180                       uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
181 {
182         dwc_cc_t *cc;
183         uint32_t uid;
184
185         if (cc_if->is_host) {
186                 uid = cc_match_cdid(cc_if, cdid);
187         }
188         else {
189                 uid = cc_match_chid(cc_if, chid);
190         }
191
192         if (uid) {
193                 DWC_DEBUGC("Replacing previous connection context id=%d name=%p name_len=%d", uid, name, length);
194                 cc = cc_find(cc_if, uid);
195         }
196         else {
197                 cc = alloc_cc(mem_ctx, name, length);
198                 cc->uid = next_uid(cc_if);
199                 DWC_CIRCLEQ_INSERT_TAIL(&cc_if->list, cc, list_entry);
200         }
201
202         DWC_MEMCPY(&(cc->chid[0]), chid, 16);
203         DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
204         DWC_MEMCPY(&(cc->ck[0]), ck, 16);
205
206         DWC_DEBUGC("Added connection context id=%d name=%p name_len=%d", cc->uid, name, length);
207         dump_bytes("CHID", cc->chid, 16);
208         dump_bytes("CDID", cc->cdid, 16);
209         dump_bytes("CK", cc->ck, 16);
210         return cc->uid;
211 }
212
213 /* Internal cc_clear */
214 static void cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
215 {
216         while (!DWC_CIRCLEQ_EMPTY(&cc_if->list)) {
217                 dwc_cc_t *cc = DWC_CIRCLEQ_FIRST(&cc_if->list);
218                 DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
219                 free_cc(mem_ctx, cc);
220         }
221 }
222
223 dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx,
224                              dwc_notifier_t *notifier, unsigned is_host)
225 {
226         dwc_cc_if_t *cc_if = NULL;
227
228         /* Allocate a common_cc_if structure */
229         cc_if = dwc_alloc(mem_ctx, sizeof(dwc_cc_if_t));
230
231         if (!cc_if)
232                 return NULL;
233
234 #if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
235         DWC_MUTEX_ALLOC_LINUX_DEBUG(cc_if->mutex);
236 #else
237         cc_if->mutex = dwc_mutex_alloc(mtx_ctx);
238 #endif
239         if (!cc_if->mutex) {
240                 dwc_free(mem_ctx, cc_if);
241                 return NULL;
242         }
243
244         DWC_CIRCLEQ_INIT(&cc_if->list);
245         cc_if->is_host = is_host;
246         cc_if->notifier = notifier;
247         return cc_if;
248 }
249
250 void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if)
251 {
252 #if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
253         DWC_MUTEX_FREE(cc_if->mutex);
254 #else
255         dwc_mutex_free(mtx_ctx, cc_if->mutex);
256 #endif
257         cc_clear(mem_ctx, cc_if);
258         dwc_free(mem_ctx, cc_if);
259 }
260
261 static void cc_changed(dwc_cc_if_t *cc_if)
262 {
263         if (cc_if->notifier) {
264                 dwc_notify(cc_if->notifier, DWC_CC_LIST_CHANGED_NOTIFICATION, cc_if);
265         }
266 }
267
268 void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
269 {
270         DWC_MUTEX_LOCK(cc_if->mutex);
271         cc_clear(mem_ctx, cc_if);
272         DWC_MUTEX_UNLOCK(cc_if->mutex);
273         cc_changed(cc_if);
274 }
275
276 int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
277                    uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
278 {
279         uint32_t uid;
280
281         DWC_MUTEX_LOCK(cc_if->mutex);
282         uid = cc_add(mem_ctx, cc_if, chid, cdid, ck, name, length);
283         DWC_MUTEX_UNLOCK(cc_if->mutex);
284         cc_changed(cc_if);
285
286         return uid;
287 }
288
289 void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id, uint8_t *chid,
290                    uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
291 {
292         dwc_cc_t* cc;
293
294         DWC_DEBUGC("Change connection context %d", id);
295
296         DWC_MUTEX_LOCK(cc_if->mutex);
297         cc = cc_find(cc_if, id);
298         if (!cc) {
299                 DWC_ERROR("Uid %d not found in cc list\n", id);
300                 DWC_MUTEX_UNLOCK(cc_if->mutex);
301                 return;
302         }
303
304         if (chid) {
305                 DWC_MEMCPY(&(cc->chid[0]), chid, 16);
306         }
307         if (cdid) {
308                 DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
309         }
310         if (ck) {
311                 DWC_MEMCPY(&(cc->ck[0]), ck, 16);
312         }
313
314         if (name) {
315                 if (cc->name) {
316                         dwc_free(mem_ctx, cc->name);
317                 }
318                 cc->name = dwc_alloc(mem_ctx, length);
319                 if (!cc->name) {
320                         DWC_ERROR("Out of memory in dwc_cc_change()\n");
321                         DWC_MUTEX_UNLOCK(cc_if->mutex);
322                         return;
323                 }
324                 cc->length = length;
325                 DWC_MEMCPY(cc->name, name, length);
326         }
327
328         DWC_MUTEX_UNLOCK(cc_if->mutex);
329
330         cc_changed(cc_if);
331
332         DWC_DEBUGC("Changed connection context id=%d\n", id);
333         dump_bytes("New CHID", cc->chid, 16);
334         dump_bytes("New CDID", cc->cdid, 16);
335         dump_bytes("New CK", cc->ck, 16);
336 }
337
338 void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id)
339 {
340         dwc_cc_t *cc;
341
342         DWC_DEBUGC("Removing connection context %d", id);
343
344         DWC_MUTEX_LOCK(cc_if->mutex);
345         cc = cc_find(cc_if, id);
346         if (!cc) {
347                 DWC_ERROR("Uid %d not found in cc list\n", id);
348                 DWC_MUTEX_UNLOCK(cc_if->mutex);
349                 return;
350         }
351
352         DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
353         DWC_MUTEX_UNLOCK(cc_if->mutex);
354         free_cc(mem_ctx, cc);
355
356         cc_changed(cc_if);
357 }
358
359 uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if, unsigned int *length)
360 {
361         uint8_t *buf, *x;
362         uint8_t zero = 0;
363         dwc_cc_t *cc;
364
365         DWC_MUTEX_LOCK(cc_if->mutex);
366         *length = cc_data_size(cc_if);
367         if (!(*length)) {
368                 DWC_MUTEX_UNLOCK(cc_if->mutex);
369                 return NULL;
370         }
371
372         DWC_DEBUGC("Creating data for saving (length=%d)", *length);
373
374         buf = dwc_alloc(mem_ctx, *length);
375         if (!buf) {
376                 *length = 0;
377                 DWC_MUTEX_UNLOCK(cc_if->mutex);
378                 return NULL;
379         }
380
381         x = buf;
382         DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
383                 DWC_MEMCPY(x, cc->chid, 16);
384                 x += 16;
385                 DWC_MEMCPY(x, cc->cdid, 16);
386                 x += 16;
387                 DWC_MEMCPY(x, cc->ck, 16);
388                 x += 16;
389                 if (cc->name) {
390                         DWC_MEMCPY(x, &cc->length, 1);
391                         x += 1;
392                         DWC_MEMCPY(x, cc->name, cc->length);
393                         x += cc->length;
394                 }
395                 else {
396                         DWC_MEMCPY(x, &zero, 1);
397                         x += 1;
398                 }
399         }
400         DWC_MUTEX_UNLOCK(cc_if->mutex);
401
402         return buf;
403 }
404
405 void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *data, uint32_t length)
406 {
407         uint8_t name_length;
408         uint8_t *name;
409         uint8_t *chid;
410         uint8_t *cdid;
411         uint8_t *ck;
412         uint32_t i = 0;
413
414         DWC_MUTEX_LOCK(cc_if->mutex);
415         cc_clear(mem_ctx, cc_if);
416
417         while (i < length) {
418                 chid = &data[i];
419                 i += 16;
420                 cdid = &data[i];
421                 i += 16;
422                 ck = &data[i];
423                 i += 16;
424
425                 name_length = data[i];
426                 i ++;
427
428                 if (name_length) {
429                         name = &data[i];
430                         i += name_length;
431                 }
432                 else {
433                         name = NULL;
434                 }
435
436                 /* check to see if we haven't overflown the buffer */
437                 if (i > length) {
438                         DWC_ERROR("Data format error while attempting to load CCs "
439                                   "(nlen=%d, iter=%d, buflen=%d).\n", name_length, i, length);
440                         break;
441                 }
442
443                 cc_add(mem_ctx, cc_if, chid, cdid, ck, name, name_length);
444         }
445         DWC_MUTEX_UNLOCK(cc_if->mutex);
446
447         cc_changed(cc_if);
448 }
449
450 uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
451 {
452         uint32_t uid = 0;
453
454         DWC_MUTEX_LOCK(cc_if->mutex);
455         uid = cc_match_chid(cc_if, chid);
456         DWC_MUTEX_UNLOCK(cc_if->mutex);
457         return uid;
458 }
459 uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
460 {
461         uint32_t uid = 0;
462
463         DWC_MUTEX_LOCK(cc_if->mutex);
464         uid = cc_match_cdid(cc_if, cdid);
465         DWC_MUTEX_UNLOCK(cc_if->mutex);
466         return uid;
467 }
468
469 uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id)
470 {
471         uint8_t *ck = NULL;
472         dwc_cc_t *cc;
473
474         DWC_MUTEX_LOCK(cc_if->mutex);
475         cc = cc_find(cc_if, id);
476         if (cc) {
477                 ck = cc->ck;
478         }
479         DWC_MUTEX_UNLOCK(cc_if->mutex);
480
481         return ck;
482
483 }
484
485 uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id)
486 {
487         uint8_t *retval = NULL;
488         dwc_cc_t *cc;
489
490         DWC_MUTEX_LOCK(cc_if->mutex);
491         cc = cc_find(cc_if, id);
492         if (cc) {
493                 retval = cc->chid;
494         }
495         DWC_MUTEX_UNLOCK(cc_if->mutex);
496
497         return retval;
498 }
499
500 uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id)
501 {
502         uint8_t *retval = NULL;
503         dwc_cc_t *cc;
504
505         DWC_MUTEX_LOCK(cc_if->mutex);
506         cc = cc_find(cc_if, id);
507         if (cc) {
508                 retval = cc->cdid;
509         }
510         DWC_MUTEX_UNLOCK(cc_if->mutex);
511
512         return retval;
513 }
514
515 uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length)
516 {
517         uint8_t *retval = NULL;
518         dwc_cc_t *cc;
519
520         DWC_MUTEX_LOCK(cc_if->mutex);
521         *length = 0;
522         cc = cc_find(cc_if, id);
523         if (cc) {
524                 *length = cc->length;
525                 retval = cc->name;
526         }
527         DWC_MUTEX_UNLOCK(cc_if->mutex);
528
529         return retval;
530 }
531
532 #endif  /* DWC_CCLIB */