summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/bestcomm
diff options
context:
space:
mode:
authorGrant Likely2008-12-21 10:54:27 +0100
committerGrant Likely2008-12-21 10:54:27 +0100
commit622882455aa718cfbd6785cc5fbc479f1d976095 (patch)
tree6c8fe006241810845419f17a80b4e82dc03aed11 /arch/powerpc/sysdev/bestcomm
parentpowerpc/mpc5200: Make internal 5200 PIC the default interrupt controller (diff)
downloadkernel-qcow2-linux-622882455aa718cfbd6785cc5fbc479f1d976095.tar.gz
kernel-qcow2-linux-622882455aa718cfbd6785cc5fbc479f1d976095.tar.xz
kernel-qcow2-linux-622882455aa718cfbd6785cc5fbc479f1d976095.zip
powerpc/mpc5200: Bugfix on handling variable sized buffer descriptors
The buffer descriptors for the ATA BestComm task are larger than the current definition for bcom_bd. This causes problems because the various bcom_... functions dereference the buffer descriptor pointer by using the array operator which doesn't work when the buffer descriptors are a different size. This patch adds the bcom_get_bd() function which uses the value in bcom_task.bd_size to calculate the offset into the BD table. This patch also changes the definition of bcom_bd to specify a data size of 0 instead of 1 so that it will never work if anyone attempts to dereference the bd list as an array (as opposed to something that might work even though it is wrong). Finally, this patch moves the definition of bcom_bd up in the file to eliminate a forward declaration. Based on patch originally written by Tim Yamin. Signed-off-by: Tim Yamin <plasm@roo.me.uk> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'arch/powerpc/sysdev/bestcomm')
-rw-r--r--arch/powerpc/sysdev/bestcomm/bestcomm.h61
1 files changed, 42 insertions, 19 deletions
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/sysdev/bestcomm/bestcomm.h
index c960a8b49655..23a95f80dfdb 100644
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.h
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.h
@@ -16,8 +16,19 @@
#ifndef __BESTCOMM_H__
#define __BESTCOMM_H__
-struct bcom_bd; /* defined later on ... */
-
+/**
+ * struct bcom_bd - Structure describing a generic BestComm buffer descriptor
+ * @status: The current status of this buffer. Exact meaning depends on the
+ * task type
+ * @data: An array of u32 extra data. Size of array is task dependant.
+ *
+ * Note: Don't dereference a bcom_bd pointer as an array. The size of the
+ * bcom_bd is variable. Use bcom_get_bd() instead.
+ */
+struct bcom_bd {
+ u32 status;
+ u32 data[0]; /* variable payload size */
+};
/* ======================================================================== */
/* Generic task management */
@@ -84,17 +95,6 @@ bcom_get_task_irq(struct bcom_task *tsk) {
/* BD based tasks helpers */
/* ======================================================================== */
-/**
- * struct bcom_bd - Structure describing a generic BestComm buffer descriptor
- * @status: The current status of this buffer. Exact meaning depends on the
- * task type
- * @data: An array of u32 whose meaning depends on the task type.
- */
-struct bcom_bd {
- u32 status;
- u32 data[1]; /* variable, but at least 1 */
-};
-
#define BCOM_BD_READY 0x40000000ul
/** _bcom_next_index - Get next input index.
@@ -140,15 +140,31 @@ bcom_queue_full(struct bcom_task *tsk)
}
/**
+ * bcom_get_bd - Get a BD from the queue
+ * @tsk: The BestComm task structure
+ * index: Index of the BD to fetch
+ */
+static inline struct bcom_bd
+*bcom_get_bd(struct bcom_task *tsk, unsigned int index)
+{
+ /* A cast to (void*) so the address can be incremented by the
+ * real size instead of by sizeof(struct bcom_bd) */
+ return ((void *)tsk->bd) + (index * tsk->bd_size);
+}
+
+/**
* bcom_buffer_done - Checks if a BestComm
* @tsk: The BestComm task structure
*/
static inline int
bcom_buffer_done(struct bcom_task *tsk)
{
+ struct bcom_bd *bd;
if (bcom_queue_empty(tsk))
return 0;
- return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
+
+ bd = bcom_get_bd(tsk, tsk->outdex);
+ return !(bd->status & BCOM_BD_READY);
}
/**
@@ -160,16 +176,21 @@ bcom_buffer_done(struct bcom_task *tsk)
static inline struct bcom_bd *
bcom_prepare_next_buffer(struct bcom_task *tsk)
{
- tsk->bd[tsk->index].status = 0; /* cleanup last status */
- return &tsk->bd[tsk->index];
+ struct bcom_bd *bd;
+
+ bd = bcom_get_bd(tsk, tsk->index);
+ bd->status = 0; /* cleanup last status */
+ return bd;
}
static inline void
bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
{
+ struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index);
+
tsk->cookie[tsk->index] = cookie;
mb(); /* ensure the bd is really up-to-date */
- tsk->bd[tsk->index].status |= BCOM_BD_READY;
+ bd->status |= BCOM_BD_READY;
tsk->index = _bcom_next_index(tsk);
if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
bcom_enable(tsk);
@@ -179,10 +200,12 @@ static inline void *
bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
{
void *cookie = tsk->cookie[tsk->outdex];
+ struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex);
+
if (p_status)
- *p_status = tsk->bd[tsk->outdex].status;
+ *p_status = bd->status;
if (p_bd)
- *p_bd = &tsk->bd[tsk->outdex];
+ *p_bd = bd;
tsk->outdex = _bcom_next_outdex(tsk);
return cookie;
}