1/*****************************************************************************/
2/*
3 *      auerbuf.c  --  Auerswald PBX/System Telephone urb list storage.
4 *
5 *      Copyright (C) 2002  Wolfgang M�es (wolfgang@iksw-muees.de)
6 *
7 *      This program is free software; you can redistribute it and/or modify
8 *      it under the terms of the GNU General Public License as published by
9 *      the Free Software Foundation; either version 2 of the License, or
10 *      (at your option) any later version.
11 *
12 *      This program is distributed in the hope that it will be useful,
13 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *      GNU General Public License for more details.
16 *
17 *      You should have received a copy of the GNU General Public License
18 *      along with this program; if not, write to the Free Software
19 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21 /*****************************************************************************/
22
23#undef DEBUG			/* include debug macros until it's done */
24#include <linux/usb.h>
25#include "auerbuf.h"
26#include <linux/slab.h>
27
28/* free a single auerbuf */
29void auerbuf_free(struct auerbuf *bp)
30{
31	if (!bp) return;
32	kfree(bp->bufp);
33	kfree(bp->dr);
34	if (bp->urbp) {
35		usb_free_urb(bp->urbp);
36	}
37	kfree(bp);
38}
39
40/* free the buffers from an auerbuf list */
41void auerbuf_free_list(struct list_head *q)
42{
43	struct list_head *tmp;
44	struct list_head *p;
45	struct auerbuf *bp;
46
47	dbg("auerbuf_free_list");
48	for (p = q->next; p != q;) {
49		bp = list_entry(p, struct auerbuf, buff_list);
50		tmp = p->next;
51		list_del(p);
52		p = tmp;
53		auerbuf_free(bp);
54	}
55}
56
57/* free all buffers from an auerbuf chain */
58void auerbuf_free_buffers(struct auerbufctl *bcp)
59{
60	unsigned long flags;
61	dbg("auerbuf_free_buffers");
62
63	spin_lock_irqsave(&bcp->lock, flags);
64
65	auerbuf_free_list(&bcp->free_buff_list);
66	auerbuf_free_list(&bcp->rec_buff_list);
67
68	spin_unlock_irqrestore(&bcp->lock, flags);
69}
70
71/* init the members of a list control block */
72void auerbuf_init(struct auerbufctl *bcp)
73{
74	dbg("auerbuf_init");
75	spin_lock_init(&bcp->lock);
76	INIT_LIST_HEAD(&bcp->free_buff_list);
77	INIT_LIST_HEAD(&bcp->rec_buff_list);
78}
79
80/* setup a list of buffers */
81/* requirement: auerbuf_init() */
82int auerbuf_setup(struct auerbufctl *bcp, unsigned int numElements,
83		  unsigned int bufsize)
84{
85	struct auerbuf *bep = NULL;
86
87	dbg("auerbuf_setup called with %d elements of %d bytes",
88	    numElements, bufsize);
89
90	/* fill the list of free elements */
91	for (; numElements; numElements--) {
92		bep =
93		    (struct auerbuf *) kmalloc(sizeof(struct auerbuf),
94					       GFP_KERNEL);
95		if (!bep)
96			goto bl_fail;
97		memset(bep, 0, sizeof(struct auerbuf));
98		bep->list = bcp;
99		INIT_LIST_HEAD(&bep->buff_list);
100		bep->bufp = (char *) kmalloc(bufsize, GFP_KERNEL);
101		if (!bep->bufp)
102			goto bl_fail;
103		bep->dr =
104		    (struct usb_ctrlrequest *)
105		    kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
106		if (!bep->dr)
107			goto bl_fail;
108		bep->urbp = usb_alloc_urb(0);
109		if (!bep->urbp)
110			goto bl_fail;
111		list_add_tail(&bep->buff_list, &bcp->free_buff_list);
112	}
113	return 0;
114
115      bl_fail:			/* not enought memory. Free allocated elements */
116	dbg("auerbuf_setup: no more memory");
117	auerbuf_free (bep);
118	auerbuf_free_buffers(bcp);
119	return -ENOMEM;
120}
121
122/* alloc a free buffer from the list. Returns NULL if no buffer available */
123struct auerbuf *auerbuf_getbuf(struct auerbufctl *bcp)
124{
125	unsigned long flags;
126	struct auerbuf *bp = NULL;
127
128	spin_lock_irqsave(&bcp->lock, flags);
129	if (!list_empty(&bcp->free_buff_list)) {
130		/* yes: get the entry */
131		struct list_head *tmp = bcp->free_buff_list.next;
132		list_del(tmp);
133		bp = list_entry(tmp, struct auerbuf, buff_list);
134	}
135	spin_unlock_irqrestore(&bcp->lock, flags);
136	return bp;
137}
138
139/* insert a used buffer into the free list */
140void auerbuf_releasebuf(struct auerbuf *bp)
141{
142	unsigned long flags;
143	struct auerbufctl *bcp = bp->list;
144	bp->retries = 0;
145
146	dbg("auerbuf_releasebuf called");
147	spin_lock_irqsave(&bcp->lock, flags);
148	list_add_tail(&bp->buff_list, &bcp->free_buff_list);
149	spin_unlock_irqrestore(&bcp->lock, flags);
150}
151