hc
2024-08-12 233ab1bd4c5697f5cdec94e60206e8c6ac609b4c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#include "wcn_usb.h"
/**
 * struct wcn_usb_store_chn2ep - Chn and ep map table.
 * @ep:    ep. chn map to.
 * @mutex: every entry map only allow one thread touch self when one time.
 */
struct wcn_usb_store_chn2ep {
   struct wcn_usb_work_data *work_data;
   struct wcn_usb_ep *ep;
   __u8 epAddress;
};
 
#define ep_get_chn2ep(x) container_of(x, struct wcn_usb_store_chn2ep, ep)
 
#define FILL_CHN2EP_MAP(channel_id, ep_address)\
   .ep = NULL, \
   .epAddress = ep_address
 
/* we need explicitly define the chn2ep table */
/* channel_id must be Continuous and begin with zero */
/* then we can say index == channel */
static struct wcn_usb_store_chn2ep chn2ep_table[] = {
   {FILL_CHN2EP_MAP(0, 0x00)},/* no use */
   {FILL_CHN2EP_MAP(1, 0x01)},
   {FILL_CHN2EP_MAP(2, 0x02)},
   {FILL_CHN2EP_MAP(3, 0x03)},
   {FILL_CHN2EP_MAP(4, 0x04)},
   {FILL_CHN2EP_MAP(5, 0x05)},
   {FILL_CHN2EP_MAP(6, 0x06)},
   {FILL_CHN2EP_MAP(7, 0x07)},
   {FILL_CHN2EP_MAP(8, 0x08)},
   {FILL_CHN2EP_MAP(9, 0x09)},
   {FILL_CHN2EP_MAP(10, 0x0A)},
   {FILL_CHN2EP_MAP(11, 0x0B)},
   {FILL_CHN2EP_MAP(12, 0x0C)},
   {FILL_CHN2EP_MAP(13, 0x0D)},
   {FILL_CHN2EP_MAP(14, 0x0E)},
   {FILL_CHN2EP_MAP(15, 0x0F)},
   {FILL_CHN2EP_MAP(16, 0x00)},/* no use */
   /* There is BUG in MUSB_SPRD's inturrpt */
#ifndef NO_EXCHANGE_CHANNEL_17
   {FILL_CHN2EP_MAP(17, 0x8A)},
#else
   {FILL_CHN2EP_MAP(17, 0x81)},
#endif
   {FILL_CHN2EP_MAP(18, 0x82)},
   {FILL_CHN2EP_MAP(19, 0x83)},
   {FILL_CHN2EP_MAP(20, 0x84)},
   {FILL_CHN2EP_MAP(21, 0x85)},
   {FILL_CHN2EP_MAP(22, 0x86)},
   {FILL_CHN2EP_MAP(23, 0x87)},
   {FILL_CHN2EP_MAP(24, 0x88)},
   {FILL_CHN2EP_MAP(25, 0x89)},
#ifndef NO_EXCHANGE_CHANNEL_17
   {FILL_CHN2EP_MAP(26, 0x81)},
#else
   {FILL_CHN2EP_MAP(26, 0x8A)},
#endif
   {FILL_CHN2EP_MAP(27, 0x8B)},
   {FILL_CHN2EP_MAP(28, 0x8C)},
   {FILL_CHN2EP_MAP(29, 0x8D)},
   {FILL_CHN2EP_MAP(30, 0x8E)},
   {FILL_CHN2EP_MAP(31, 0x8F)},
};
 
#ifndef array_size
#define array_size(x)    (sizeof(x) / sizeof((x)[0]))
#endif
 
#define chn2ep_table_size()\
   array_size(chn2ep_table)
 
static struct wcn_usb_store_chn2ep *wcn_usb_store_get_chn2ep(int index)
{
   struct wcn_usb_store_chn2ep *ret;
   int table_size = chn2ep_table_size();
 
   /* May be we can call unlike */
   if (index >= table_size) {
       wcn_usb_err("%s index[%d] is invalid(table size %d)\n",
                  __func__, index, table_size);
       return NULL;
   }
 
   ret = chn2ep_table + index;
 
   return ret;
}
 
/**
 * wcn_usb_store_get_epFRchn - get a ep describe from a channel id
 * @id: The key we looking for.
 *
 * return: if we find, we return. else NULL.
 */
struct wcn_usb_ep *wcn_usb_store_get_epFRchn(int channel)
{
   struct wcn_usb_store_chn2ep *chn2ep;
 
   chn2ep = wcn_usb_store_get_chn2ep(channel);
   if (chn2ep == NULL)
       return NULL;
   return chn2ep->ep;
}
 
struct wcn_usb_work_data *wcn_usb_store_get_channel_info(int channel)
{
   struct wcn_usb_store_chn2ep *chn2ep;
 
   chn2ep = wcn_usb_store_get_chn2ep(channel);
   if (chn2ep == NULL)
       return NULL;
   return chn2ep->work_data;
}
 
/**
 * wcn_usb_store_addr2chn() - get a chn describe from a ep
 * @address: The key we looking for.
 *
 * return: if we find, we return a chnnal id. else 0;
 *
 * NOTE: If we take long time in this function, we need build a table for
 * epAddress to chn id.
 */
int wcn_usb_store_addr2chn(__u8 epAddress)
{
   int i;
   int table_size;
   struct wcn_usb_store_chn2ep *chn2ep;
 
   table_size = chn2ep_table_size();
 
   for (i = 0; i < table_size; i++) {
       chn2ep = wcn_usb_store_get_chn2ep(i);
       if (chn2ep->epAddress == epAddress)
           return i;
   }
   return -1;
}
 
__u8 wcn_usb_store_chn2addr(int channel)
{
   struct wcn_usb_store_chn2ep *chn2ep;
 
   chn2ep = wcn_usb_store_get_chn2ep(channel);
   if (!chn2ep)
       return 0;
 
   return chn2ep->epAddress;
}
 
 
int wcn_usb_store_travel_ep(ep_handle_cb cb, void *pdata)
{
   int i;
   size_t table_size;
   struct wcn_usb_store_chn2ep *chn2ep;
   int ret;
 
   table_size = chn2ep_table_size();
 
   for (i = 0; i < table_size; i++) {
       chn2ep = wcn_usb_store_get_chn2ep(i);
       ret = cb(chn2ep->ep, pdata);
       if (ret)
           return ret;
   }
 
   return ret;
}
 
static void wcn_usb_state_init(void);
/**
 * wcn_usb_store_init() - init wcn_usb_store memory.
 * @void: void.
 *
 * return: zero for success, or a error number reutrn.
 *
 * Note: This function must be called before the interface driver probe that
 * interface belong to sprd wcn bus usb.
 */
int wcn_usb_store_init(void)
{
   struct wcn_usb_store_chn2ep *chn2ep;
   int table_size = chn2ep_table_size();
   int i;
 
   for (i = 0; i < table_size; i++) {
       chn2ep = wcn_usb_store_get_chn2ep(i);
       WARN_ON(chn2ep->ep != NULL);
 
       chn2ep->ep = kzalloc(sizeof(struct wcn_usb_ep), GFP_KERNEL);
       if (!chn2ep->ep)
           return -ENOMEM;
 
       wcn_usb_ep_init(chn2ep->ep, i);
 
       chn2ep->work_data = kzalloc(sizeof(struct wcn_usb_work_data),
                   GFP_KERNEL);
       if (!chn2ep->work_data)
           return -ENOMEM;
 
       wcn_usb_work_data_init(chn2ep->work_data, i);
   }
 
   wcn_usb_state_init();
   wcn_usb_info("%s success\n", __func__);
   return 0;
}
 
static int wcn_usb_work_data_reset(void)
{
   struct wcn_usb_store_chn2ep *chn2ep;
   int table_size = chn2ep_table_size();
   int i;
 
   for (i = 0; i < table_size; i++) {
       chn2ep = wcn_usb_store_get_chn2ep(i);
 
       chn2ep->work_data->report_num_last = 0;
       chn2ep->work_data->transfer_remains = 0;
       chn2ep->work_data->report_num = 0;
   }
 
   wcn_usb_info("%s success\n", __func__);
   return 0;
}
 
 
/**
 * wcn_usb_store_delet() - free wcn_usb_store memory.
 * @void: void.
 *
 * free wcn_usb_store memory.
 *
 * return void.
 *
 * Note: This function must be called later the interface driver free!
 */
void wcn_usb_store_delet(void)
{
   struct wcn_usb_store_chn2ep *chn2ep;
   int table_size = chn2ep_table_size();
   int i;
 
   for (i = 0; i < table_size; i++) {
       chn2ep = wcn_usb_store_get_chn2ep(i);
       kfree(chn2ep->ep);
   }
}
 
 
static ATOMIC_NOTIFIER_HEAD(wcn_usb_state_list);
 
int wcn_usb_state_sent_event(enum wcn_usb_event event)
{
   wcn_usb_info("%s event:0x%x\n", __func__, event);
 
   return atomic_notifier_call_chain(&wcn_usb_state_list, event, NULL);
}
 
int wcn_usb_state_register(struct notifier_block *nb)
{
   return atomic_notifier_chain_register(&wcn_usb_state_list, nb);
}
 
int wcn_usb_state_unregister(struct notifier_block *nb)
{
   return atomic_notifier_chain_unregister(&wcn_usb_state_list, nb);
}
 
static struct wcn_usb_state {
   struct notifier_block nb;
   unsigned int interface_pluged:3;
   unsigned int downloaded:1;
   unsigned int pwr_state:1;
   unsigned int cp_ready:1;
   unsigned int errored:1;
   unsigned int :0;
} wcn_usb_state;
 
int wcn_usb_state_get(enum wcn_usb_event event)
{
   struct wcn_usb_state *state = &wcn_usb_state;
 
   unsigned int interface_id;
 
   switch (event) {
   case interface_0_plug:
   case interface_1_plug:
   case interface_2_plug:
       interface_id = event - interface_plug_base;
       return (state->interface_pluged & (1 << interface_id)) != 0;
   case dev_plug_fully:
       return state->interface_pluged == 0x7;
   case interface_0_unplug:
   case interface_1_unplug:
   case interface_2_unplug:
       interface_id = event - interface_unplug_base;
       return (state->interface_pluged & (1 << interface_id)) == 0;
   case dev_unplug_fully:
       return state->interface_pluged == 0x7;
   case download_over:
       return state->downloaded == 1;
   case pwr_state:
       return state->pwr_state == 1;
   case cp_ready:
       return state->cp_ready == 1;
   case error_happen:
       return state->errored == 1;
   case error_clean:
       return state->errored == 0;
   default:
       break;
   }
   return 0;
}
 
static int wcn_usb_state_nb_cb(struct notifier_block *nb,
       unsigned long action, void *data)
{
   struct wcn_usb_state *state =
       container_of(nb, struct wcn_usb_state, nb);
   unsigned int interface_id;
   unsigned int interface_pluged_old;
 
   switch (action) {
   case interface_0_plug:
   case interface_1_plug:
   case interface_2_plug:
       interface_id = action - interface_plug_base;
       interface_pluged_old = state->interface_pluged;
       state->interface_pluged |= 1 << interface_id;
       if (state->interface_pluged == 7 && interface_pluged_old != 7)
           wcn_usb_state_sent_event(dev_plug_fully);
       break;
   case dev_plug_fully:
       break;
   case interface_0_unplug:
   case interface_1_unplug:
   case interface_2_unplug:
       interface_id = action - interface_unplug_base;
       interface_pluged_old = state->interface_pluged;
       state->interface_pluged &= ~(1 << interface_id);
       if (state->interface_pluged == 0 && interface_pluged_old != 0)
           wcn_usb_state_sent_event(dev_unplug_fully);
       break;
   case dev_unplug_fully:
       state->downloaded = 0;
       break;
   case download_over:
       state->downloaded = 1;
       break;
   case pwr_on:
       state->pwr_state = 1;
       break;
   case pwr_off:
       state->pwr_state = 0;
       wcn_usb_work_data_reset();
       break;
   case cp_ready:
       state->cp_ready = 1;
       break;
   case error_happen:
       state->errored = 1;
       break;
   case error_clean:
       state->errored = 0;
       break;
   default:
       break;
   }
   return NOTIFY_OK;
}
 
static void wcn_usb_state_init(void)
{
   wcn_usb_state.nb.notifier_call = wcn_usb_state_nb_cb;
   wcn_usb_state_register(&wcn_usb_state.nb);
}