aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/shost/shost_scheduler.c
blob: 17392980cb922b6e1c09948d3bf4f01907553b5b (plain)
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/****************************************************************************
 *  (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
 *
 *  [File Name]   : Scheduler.c
 *  [Description] : The source file implements the internal
 *		functions of Scheduler.
 *  [Author]      : Yang Soon Yeal { syatom.yang@samsung.com }
 *  [Department]  : System LSI Division/System SW Lab
 *  [Created Date]: 2009/2/10
 *  [Revision History]
 *      (1) 2008/06/03   by Yang Soon Yeal { syatom.yang@samsung.com }
 *          - Created this file and implements functions of Scheduler
 *	 -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n
 *				: Optimizing for performance \n
 *
 ****************************************************************************/
/****************************************************************************
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
****************************************************************************/

#include "shost.h"

#include "shost_readyq.c"
#include "shost_schedulerlib.c"

void init_scheduler(void)
{
	/*init_scheduling();*/
	init_transfer_ready_q();
}

/******************************************************************************/
/*!
 * @name int reserve_used_resource_for_periodic(u32	usb_time)
 *
 * @brief this function reserves the necessary resource of USB Transfer
 * for Periodic Transfer.
 * So, this function firstly checks there ares some available USB Time
 * and Channel resource for USB Transfer.
 * if there exists necessary resources for Periodic Transfer,
 * then reserves the resource.
 *
 * @param	[IN]	usb_time
 * - indicates the USB Time for the USB Transfer.
 *
 * @return	USB_ERR_SUCCESS		-	if success to insert pInsertED to S3CScheduler.
 * USB_ERR_NO_BANDWIDTH	-	if fail to reserve the USB Bandwidth.
 * USB_ERR_NO_CHANNEL		-	if fail to reserve the Channel.
 */
/******************************************************************************/

int	reserve_used_resource_for_periodic(u32 usb_time,
		u8 dev_speed, u8 trans_type)
{
	if (inc_perio_bus_time(usb_time, dev_speed) == USB_ERR_SUCCESS) {

		if (inc_perio_chnum() == USB_ERR_SUCCESS) {

			otg_usbcore_inc_usb_bandwidth(usb_time);
			otg_usbcore_inc_periodic_transfer_cnt(trans_type);

			return USB_ERR_SUCCESS;

		} else {
			dec_perio_bus_time(usb_time);
			return USB_ERR_NO_CHANNEL;
		}

	} else
		return USB_ERR_NO_BANDWIDTH;
}

/******************************************************************************/
/*!
 * @name int free_usb_resource_for_periodic(ed_t *pFreeED)
 *
 * @brief this function frees the resources to be allocated
 * to pFreeED at S3CScheduler.
 * that is, this functions only releases the resources
 * to be allocated by S3C6400Scheduler.
 *
 * @param	[IN]	pFreeED
 * - indicates ed_t to have the information of the resource to be released.
 *
 * @return	USB_ERR_SUCCESS	-	if success to free the USB Resource.
 * USB_ERR_FAIL		-	if fail to free the USB Resrouce.
 */
/******************************************************************************/
int	free_usb_resource_for_periodic(
		u32	free_usb_time, u8 free_chnum, u8 trans_type)
{
	if (dec_perio_bus_time(free_usb_time) == USB_ERR_SUCCESS) {

		if (dec_perio_chnum() == USB_ERR_SUCCESS) {

			if (free_chnum != CH_NONE) {
				oci_channel_dealloc(free_chnum);
				set_transferring_td_array(free_chnum, 0);
			}

			otg_usbcore_des_usb_bandwidth(free_usb_time);
			otg_usbcore_des_periodic_transfer_cnt(trans_type);

			return USB_ERR_SUCCESS;
		}
	}
	return USB_ERR_FAIL;
}

/******************************************************************************/
/*!
 * @name int remove_ed_from_scheduler(ed_t *remove_ed)
 *
 * @brief this function just remove the remove_ed from TransferReadyQ.
 * So if you want to stop the USB Tranfer of remove_ed
 * or release the releated resources.
 * you should call another functions of S3CScheduler.
 *
 * @param	[IN]	remove_ed
 * - indicates ed_t to be removed from TransferReadyQ.
 *
 * @return
 * USB_ERR_SUCCESS	-if success to remove the remove_ed from TransferReadyQ.
 * USB_ERR_FAIL		-if fail to remove the remove_ed from TransferReadyQ.
 */
/******************************************************************************/
int	remove_ed_from_scheduler(struct ed *remove_ed)
{
	if (remove_ed->ed_status.is_in_transfer_ready_q) {
		remove_ed_from_ready_q(remove_ed);
		remove_ed->ed_status.is_in_transfer_ready_q = false;

		return USB_ERR_SUCCESS;
	} else
		return USB_ERR_FAIL;
}

/******************************************************************************/
/*!
 * @name int cancel_to_transfer_td(struct sec_otghost *otghost,
 * struct td *cancel_td)
 *
 * @brief this function stop to execute the USB Transfer of cancel_td and
 * release the Channel Resources to be allocated the cancel_td ,
 * if the Transfer Type of cancel_td is NonPeriodic Transfer.
 * this function don't release any usb resources(Channel, USB Bandwidth)
 * for Periodic Transfer.
 * if you want to release some usb resources for a periodic Transfer,
 * you should call the free_usb_resource_for_periodic()
 *
 * @param	[IN]	cancel_td =	indicates the struct td to be canceled.
 *
 * @return
 * USB_ERR_SUCCESS-if success to cancel the USB Transfer of cancel_td.
 * USB_ERR_FAIL	-if fail to cancel the USB Transfer of cancel_td.
 */
/******************************************************************************/
int cancel_to_transfer_td(struct sec_otghost *otghost, struct td *cancel_td)
{
	if (cancel_td->is_transfer_done)
		return USB_ERR_FAIL;

	if (cancel_td->is_transferring) {
		int err;
		err = oci_stop_transfer(otghost, cancel_td->cur_stransfer.
				alloc_chnum);

		if (err == USB_ERR_SUCCESS) {

			set_transferring_td_array(cancel_td->cur_stransfer.
					alloc_chnum, 0);

			cancel_td->cur_stransfer.alloc_chnum = CH_NONE;
			cancel_td->is_transferring = false;
			cancel_td->parent_ed_p->ed_status.is_in_transferring =
				false;
			cancel_td->parent_ed_p->ed_status.in_transferring_td =
				0;
			cancel_td->parent_ed_p->is_need_to_insert_scheduler =
				true;

			if (cancel_td->cur_stransfer.ed_desc_p->
					endpoint_type == BULK_TRANSFER ||
				cancel_td->cur_stransfer.ed_desc_p->
					endpoint_type == CONTROL_TRANSFER) {

				dec_nonperio_chnum();
			}
			return err;
		} else
			return err;
	} else
		return USB_ERR_FAIL;

	return USB_ERR_SUCCESS;
}


/******************************************************************************/
/*!
 * @name int retransmit(struct sec_otghost *otghost, struct td *retrasmit_td)
 *
 * @brief this function retransmits the retrasmit_td immediately.
 * So, the Channel of pRetransmitted is reused for retransmittion.
 *
 * @param	[IN]	retrasmit_td
 * - indicates the pointer ot the struct td to be retransmitted.
 *
 * @return	USB_ERR_SUCCESS	-	if success to retransmit the retrasmit_td.
 * USB_ERR_FAIL		-	if fail to retransmit the retrasmit_td.
 */
/******************************************************************************/
int retransmit(struct sec_otghost *otghost, struct td *retrasmit_td)
{
	u32 td_addr = 0;

	if (get_transferring_td_array(retrasmit_td->cur_stransfer.
			alloc_chnum, &td_addr) == USB_ERR_SUCCESS) {

		if (td_addr == (u32)retrasmit_td) {

			if (oci_start_transfer(otghost,
				&retrasmit_td->cur_stransfer) ==
				retrasmit_td->cur_stransfer.alloc_chnum) {

				retrasmit_td->is_transferring = true;
				retrasmit_td->parent_ed_p->ed_status.
					in_transferring_td = (u32)retrasmit_td;
				retrasmit_td->parent_ed_p->ed_status.
					is_in_transfer_ready_q	= false;
				retrasmit_td->parent_ed_p->ed_status.
					is_in_transferring	= true;
			}

		} else
			return  USB_ERR_FAIL;
	} else
		return  USB_ERR_FAIL;

	return USB_ERR_SUCCESS;

}

/******************************************************************************/
/*!
 * @name int reschedule(struct td *reschedule_td)
 *
 * @brief this function re-schedules the reschedule_td.
 * So, the Channel of pRescheuleTD is released
 * and reschedule_td is inserted to TransferReadyQ.
 *
 * @param	[IN]	reschedule_td
 * - indicates the pointer ot the struct td to be rescheduled.
 *
 * @return	USB_ERR_SUCCESS	-if success to re-schedule the reschedule_td.
 * USB_ERR_FAIL		-if fail to re-schedule the reschedule_td.
 */
/******************************************************************************/
int reschedule(struct td *reschedule_td)
{
	u32	td_addr;

	if (get_transferring_td_array(reschedule_td->cur_stransfer.
				alloc_chnum, &td_addr) == USB_ERR_SUCCESS) {
		if ((u32)reschedule_td == td_addr) {
			set_transferring_td_array(reschedule_td->cur_stransfer.
					alloc_chnum, 0);
			oci_channel_dealloc(reschedule_td->cur_stransfer.
					alloc_chnum);

			reschedule_td->cur_stransfer.alloc_chnum = CH_NONE;
			reschedule_td->parent_ed_p->
				is_need_to_insert_scheduler = true;
			reschedule_td->parent_ed_p->ed_status.
				in_transferring_td = 0;

			if (reschedule_td->parent_ed_p->ed_desc.
					endpoint_type == BULK_TRANSFER ||
				reschedule_td->parent_ed_p->ed_desc.
					endpoint_type == CONTROL_TRANSFER) {
				/* Increase the available Channel */
				dec_nonperio_chnum();

			}

			insert_ed_to_ready_q(reschedule_td->parent_ed_p, false);
			reschedule_td->parent_ed_p->ed_status.
				is_in_transfer_ready_q = true;

		}
		/* this case is not support....
		else {
		}
		*/
	}

	return USB_ERR_SUCCESS;
}

/******************************************************************************/
/*!
 * @name int deallocate(struct td  *deallocate_td)
 *
 * @brief this function frees resources to be allocated deallocate_td
 * by S3CScheduler.
 * this function just free the resource by S3CScheduler.
 * that is, Channel Resource.
 * if there are another struct td at ed_t,
 * deallocate() insert the ed_t to TransferReadyQ.
 *
 * @param	[IN]	deallocate_td
 * - indicates the pointer ot the struct td to be deallocated.
 *
 * @return
 * USB_ERR_SUCCESS -if success to dealloate the resources for the deallocate_td.
 * USB_ERR_FAIL -if fail to dealloate the resources for the deallocate_td.
 */
/******************************************************************************/
int	deallocate(struct td  *deallocate_td)
{
	u32 td_addr;

	if (get_transferring_td_array(deallocate_td->cur_stransfer.alloc_chnum,
				&td_addr) == USB_ERR_SUCCESS) {
		if ((u32)deallocate_td == td_addr) {

			set_transferring_td_array(deallocate_td->
					cur_stransfer.alloc_chnum, 0);

			oci_channel_dealloc(deallocate_td->cur_stransfer.
					alloc_chnum);

			deallocate_td->cur_stransfer.alloc_chnum = CH_NONE;

			if (deallocate_td->parent_ed_p->ed_desc.
					endpoint_type == BULK_TRANSFER ||
				deallocate_td->parent_ed_p->ed_desc.
					endpoint_type == CONTROL_TRANSFER) {
				/* Increase the available Channel */
				dec_nonperio_chnum();
			}

			deallocate_td->parent_ed_p->is_need_to_insert_scheduler
				= true;

			if (deallocate_td->parent_ed_p->num_td) {
				/* insert ed_t to TransferReadyQ. */
				insert_ed_to_ready_q(deallocate_td->parent_ed_p,
						false);
				deallocate_td->parent_ed_p->ed_status.
					is_in_transfer_ready_q	= true;
				deallocate_td->parent_ed_p->
					is_need_to_insert_scheduler = false;
			}
			return USB_ERR_SUCCESS;

		} else
			return USB_ERR_FAIL;
	} else
			return USB_ERR_FAIL;

}

/* TBD.... */
/* transferchecker-common.c
 hcd.c */
void do_schedule(struct sec_otghost *otghost)
{
	if (get_avail_chnum()) {
		do_periodic_schedule(otghost);
		do_nonperiodic_schedule(otghost);
	}
}


/******************************************************************************/
/*!
 * @name int get_td_info(u8		chnum,
 *	unsigned int *td_addr_p)
 *
 * @brief this function returns the pointer of
 *	struct td at TransferringTDArray[chnum]
 *
 * @param	[IN]	chnum
 * - indicates the index of TransferringTDArray
 *		to include the address of struct td which we gets
 *		[OUT]	td_addr_p
 * - indicate pointer to store the address of struct td.
 *
 * @return
 * USB_ERR_SUCCESS	-if success to get the address of struct td.
 * USB_ERR_FAIL-if fail to get the address of struct td.
 */
/******************************************************************************/
/* transferchecker-common.c */
int get_td_info(u8 chnum, unsigned int *td_addr_p)
{
	u32	td_addr;

	if (get_transferring_td_array(chnum, &td_addr) == USB_ERR_SUCCESS) {
		*td_addr_p = td_addr;
		return USB_ERR_SUCCESS;
	}

	return USB_ERR_FAIL;
}