diff options
Diffstat (limited to 'arch/arm/plat-omap/mailbox.c')
-rw-r--r-- | arch/arm/plat-omap/mailbox.c | 248 |
1 files changed, 118 insertions, 130 deletions
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index 08a2df766289..d2fafb892f7f 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c | |||
@@ -21,19 +21,26 @@ | |||
21 | * | 21 | * |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/interrupt.h> | 24 | #include <linux/interrupt.h> |
26 | #include <linux/device.h> | 25 | #include <linux/spinlock.h> |
26 | #include <linux/mutex.h> | ||
27 | #include <linux/delay.h> | 27 | #include <linux/delay.h> |
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/kfifo.h> | ||
30 | #include <linux/err.h> | ||
29 | 31 | ||
30 | #include <plat/mailbox.h> | 32 | #include <plat/mailbox.h> |
31 | 33 | ||
32 | static struct workqueue_struct *mboxd; | 34 | static struct workqueue_struct *mboxd; |
33 | static struct omap_mbox *mboxes; | 35 | static struct omap_mbox **mboxes; |
34 | static DEFINE_RWLOCK(mboxes_lock); | 36 | static bool rq_full; |
35 | 37 | ||
36 | static int mbox_configured; | 38 | static int mbox_configured; |
39 | static DEFINE_MUTEX(mbox_configured_lock); | ||
40 | |||
41 | static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE; | ||
42 | module_param(mbox_kfifo_size, uint, S_IRUGO); | ||
43 | MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)"); | ||
37 | 44 | ||
38 | /* Mailbox FIFO handle functions */ | 45 | /* Mailbox FIFO handle functions */ |
39 | static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) | 46 | static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) |
@@ -67,7 +74,7 @@ static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) | |||
67 | /* | 74 | /* |
68 | * message sender | 75 | * message sender |
69 | */ | 76 | */ |
70 | static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) | 77 | static int __mbox_poll_for_space(struct omap_mbox *mbox) |
71 | { | 78 | { |
72 | int ret = 0, i = 1000; | 79 | int ret = 0, i = 1000; |
73 | 80 | ||
@@ -78,49 +85,50 @@ static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) | |||
78 | return -1; | 85 | return -1; |
79 | udelay(1); | 86 | udelay(1); |
80 | } | 87 | } |
81 | mbox_fifo_write(mbox, msg); | ||
82 | return ret; | 88 | return ret; |
83 | } | 89 | } |
84 | 90 | ||
85 | |||
86 | int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) | 91 | int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) |
87 | { | 92 | { |
93 | struct omap_mbox_queue *mq = mbox->txq; | ||
94 | int ret = 0, len; | ||
88 | 95 | ||
89 | struct request *rq; | 96 | spin_lock(&mq->lock); |
90 | struct request_queue *q = mbox->txq->queue; | ||
91 | 97 | ||
92 | rq = blk_get_request(q, WRITE, GFP_ATOMIC); | 98 | if (kfifo_avail(&mq->fifo) < sizeof(msg)) { |
93 | if (unlikely(!rq)) | 99 | ret = -ENOMEM; |
94 | return -ENOMEM; | 100 | goto out; |
101 | } | ||
102 | |||
103 | len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); | ||
104 | WARN_ON(len != sizeof(msg)); | ||
95 | 105 | ||
96 | blk_insert_request(q, rq, 0, (void *) msg); | ||
97 | tasklet_schedule(&mbox->txq->tasklet); | 106 | tasklet_schedule(&mbox->txq->tasklet); |
98 | 107 | ||
99 | return 0; | 108 | out: |
109 | spin_unlock(&mq->lock); | ||
110 | return ret; | ||
100 | } | 111 | } |
101 | EXPORT_SYMBOL(omap_mbox_msg_send); | 112 | EXPORT_SYMBOL(omap_mbox_msg_send); |
102 | 113 | ||
103 | static void mbox_tx_tasklet(unsigned long tx_data) | 114 | static void mbox_tx_tasklet(unsigned long tx_data) |
104 | { | 115 | { |
105 | int ret; | ||
106 | struct request *rq; | ||
107 | struct omap_mbox *mbox = (struct omap_mbox *)tx_data; | 116 | struct omap_mbox *mbox = (struct omap_mbox *)tx_data; |
108 | struct request_queue *q = mbox->txq->queue; | 117 | struct omap_mbox_queue *mq = mbox->txq; |
109 | 118 | mbox_msg_t msg; | |
110 | while (1) { | 119 | int ret; |
111 | |||
112 | rq = blk_fetch_request(q); | ||
113 | |||
114 | if (!rq) | ||
115 | break; | ||
116 | 120 | ||
117 | ret = __mbox_msg_send(mbox, (mbox_msg_t)rq->special); | 121 | while (kfifo_len(&mq->fifo)) { |
118 | if (ret) { | 122 | if (__mbox_poll_for_space(mbox)) { |
119 | omap_mbox_enable_irq(mbox, IRQ_TX); | 123 | omap_mbox_enable_irq(mbox, IRQ_TX); |
120 | blk_requeue_request(q, rq); | 124 | break; |
121 | return; | ||
122 | } | 125 | } |
123 | blk_end_request_all(rq, 0); | 126 | |
127 | ret = kfifo_out(&mq->fifo, (unsigned char *)&msg, | ||
128 | sizeof(msg)); | ||
129 | WARN_ON(ret != sizeof(msg)); | ||
130 | |||
131 | mbox_fifo_write(mbox, msg); | ||
124 | } | 132 | } |
125 | } | 133 | } |
126 | 134 | ||
@@ -131,36 +139,21 @@ static void mbox_rx_work(struct work_struct *work) | |||
131 | { | 139 | { |
132 | struct omap_mbox_queue *mq = | 140 | struct omap_mbox_queue *mq = |
133 | container_of(work, struct omap_mbox_queue, work); | 141 | container_of(work, struct omap_mbox_queue, work); |
134 | struct omap_mbox *mbox = mq->queue->queuedata; | ||
135 | struct request_queue *q = mbox->rxq->queue; | ||
136 | struct request *rq; | ||
137 | mbox_msg_t msg; | 142 | mbox_msg_t msg; |
138 | unsigned long flags; | 143 | int len; |
139 | 144 | ||
140 | while (1) { | 145 | while (kfifo_len(&mq->fifo) >= sizeof(msg)) { |
141 | spin_lock_irqsave(q->queue_lock, flags); | 146 | len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); |
142 | rq = blk_fetch_request(q); | 147 | WARN_ON(len != sizeof(msg)); |
143 | spin_unlock_irqrestore(q->queue_lock, flags); | ||
144 | if (!rq) | ||
145 | break; | ||
146 | 148 | ||
147 | msg = (mbox_msg_t)rq->special; | 149 | if (mq->callback) |
148 | blk_end_request_all(rq, 0); | 150 | mq->callback((void *)msg); |
149 | mbox->rxq->callback((void *)msg); | ||
150 | } | 151 | } |
151 | } | 152 | } |
152 | 153 | ||
153 | /* | 154 | /* |
154 | * Mailbox interrupt handler | 155 | * Mailbox interrupt handler |
155 | */ | 156 | */ |
156 | static void mbox_txq_fn(struct request_queue *q) | ||
157 | { | ||
158 | } | ||
159 | |||
160 | static void mbox_rxq_fn(struct request_queue *q) | ||
161 | { | ||
162 | } | ||
163 | |||
164 | static void __mbox_tx_interrupt(struct omap_mbox *mbox) | 157 | static void __mbox_tx_interrupt(struct omap_mbox *mbox) |
165 | { | 158 | { |
166 | omap_mbox_disable_irq(mbox, IRQ_TX); | 159 | omap_mbox_disable_irq(mbox, IRQ_TX); |
@@ -170,19 +163,22 @@ static void __mbox_tx_interrupt(struct omap_mbox *mbox) | |||
170 | 163 | ||
171 | static void __mbox_rx_interrupt(struct omap_mbox *mbox) | 164 | static void __mbox_rx_interrupt(struct omap_mbox *mbox) |
172 | { | 165 | { |
173 | struct request *rq; | 166 | struct omap_mbox_queue *mq = mbox->rxq; |
174 | mbox_msg_t msg; | 167 | mbox_msg_t msg; |
175 | struct request_queue *q = mbox->rxq->queue; | 168 | int len; |
176 | 169 | ||
177 | while (!mbox_fifo_empty(mbox)) { | 170 | while (!mbox_fifo_empty(mbox)) { |
178 | rq = blk_get_request(q, WRITE, GFP_ATOMIC); | 171 | if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { |
179 | if (unlikely(!rq)) | 172 | omap_mbox_disable_irq(mbox, IRQ_RX); |
173 | rq_full = true; | ||
180 | goto nomem; | 174 | goto nomem; |
175 | } | ||
181 | 176 | ||
182 | msg = mbox_fifo_read(mbox); | 177 | msg = mbox_fifo_read(mbox); |
183 | 178 | ||
179 | len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); | ||
180 | WARN_ON(len != sizeof(msg)); | ||
184 | 181 | ||
185 | blk_insert_request(q, rq, 0, (void *)msg); | ||
186 | if (mbox->ops->type == OMAP_MBOX_TYPE1) | 182 | if (mbox->ops->type == OMAP_MBOX_TYPE1) |
187 | break; | 183 | break; |
188 | } | 184 | } |
@@ -207,11 +203,9 @@ static irqreturn_t mbox_interrupt(int irq, void *p) | |||
207 | } | 203 | } |
208 | 204 | ||
209 | static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, | 205 | static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, |
210 | request_fn_proc *proc, | ||
211 | void (*work) (struct work_struct *), | 206 | void (*work) (struct work_struct *), |
212 | void (*tasklet)(unsigned long)) | 207 | void (*tasklet)(unsigned long)) |
213 | { | 208 | { |
214 | struct request_queue *q; | ||
215 | struct omap_mbox_queue *mq; | 209 | struct omap_mbox_queue *mq; |
216 | 210 | ||
217 | mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); | 211 | mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); |
@@ -220,11 +214,8 @@ static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, | |||
220 | 214 | ||
221 | spin_lock_init(&mq->lock); | 215 | spin_lock_init(&mq->lock); |
222 | 216 | ||
223 | q = blk_init_queue(proc, &mq->lock); | 217 | if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL)) |
224 | if (!q) | ||
225 | goto error; | 218 | goto error; |
226 | q->queuedata = mbox; | ||
227 | mq->queue = q; | ||
228 | 219 | ||
229 | if (work) | 220 | if (work) |
230 | INIT_WORK(&mq->work, work); | 221 | INIT_WORK(&mq->work, work); |
@@ -239,7 +230,7 @@ error: | |||
239 | 230 | ||
240 | static void mbox_queue_free(struct omap_mbox_queue *q) | 231 | static void mbox_queue_free(struct omap_mbox_queue *q) |
241 | { | 232 | { |
242 | blk_cleanup_queue(q->queue); | 233 | kfifo_free(&q->fifo); |
243 | kfree(q); | 234 | kfree(q); |
244 | } | 235 | } |
245 | 236 | ||
@@ -248,35 +239,35 @@ static int omap_mbox_startup(struct omap_mbox *mbox) | |||
248 | int ret = 0; | 239 | int ret = 0; |
249 | struct omap_mbox_queue *mq; | 240 | struct omap_mbox_queue *mq; |
250 | 241 | ||
251 | if (likely(mbox->ops->startup)) { | 242 | if (mbox->ops->startup) { |
252 | write_lock(&mboxes_lock); | 243 | mutex_lock(&mbox_configured_lock); |
253 | if (!mbox_configured) | 244 | if (!mbox_configured) |
254 | ret = mbox->ops->startup(mbox); | 245 | ret = mbox->ops->startup(mbox); |
255 | 246 | ||
256 | if (unlikely(ret)) { | 247 | if (ret) { |
257 | write_unlock(&mboxes_lock); | 248 | mutex_unlock(&mbox_configured_lock); |
258 | return ret; | 249 | return ret; |
259 | } | 250 | } |
260 | mbox_configured++; | 251 | mbox_configured++; |
261 | write_unlock(&mboxes_lock); | 252 | mutex_unlock(&mbox_configured_lock); |
262 | } | 253 | } |
263 | 254 | ||
264 | ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, | 255 | ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, |
265 | mbox->name, mbox); | 256 | mbox->name, mbox); |
266 | if (unlikely(ret)) { | 257 | if (ret) { |
267 | printk(KERN_ERR | 258 | printk(KERN_ERR |
268 | "failed to register mailbox interrupt:%d\n", ret); | 259 | "failed to register mailbox interrupt:%d\n", ret); |
269 | goto fail_request_irq; | 260 | goto fail_request_irq; |
270 | } | 261 | } |
271 | 262 | ||
272 | mq = mbox_queue_alloc(mbox, mbox_txq_fn, NULL, mbox_tx_tasklet); | 263 | mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); |
273 | if (!mq) { | 264 | if (!mq) { |
274 | ret = -ENOMEM; | 265 | ret = -ENOMEM; |
275 | goto fail_alloc_txq; | 266 | goto fail_alloc_txq; |
276 | } | 267 | } |
277 | mbox->txq = mq; | 268 | mbox->txq = mq; |
278 | 269 | ||
279 | mq = mbox_queue_alloc(mbox, mbox_rxq_fn, mbox_rx_work, NULL); | 270 | mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); |
280 | if (!mq) { | 271 | if (!mq) { |
281 | ret = -ENOMEM; | 272 | ret = -ENOMEM; |
282 | goto fail_alloc_rxq; | 273 | goto fail_alloc_rxq; |
@@ -290,7 +281,7 @@ static int omap_mbox_startup(struct omap_mbox *mbox) | |||
290 | fail_alloc_txq: | 281 | fail_alloc_txq: |
291 | free_irq(mbox->irq, mbox); | 282 | free_irq(mbox->irq, mbox); |
292 | fail_request_irq: | 283 | fail_request_irq: |
293 | if (unlikely(mbox->ops->shutdown)) | 284 | if (mbox->ops->shutdown) |
294 | mbox->ops->shutdown(mbox); | 285 | mbox->ops->shutdown(mbox); |
295 | 286 | ||
296 | return ret; | 287 | return ret; |
@@ -298,31 +289,20 @@ static int omap_mbox_startup(struct omap_mbox *mbox) | |||
298 | 289 | ||
299 | static void omap_mbox_fini(struct omap_mbox *mbox) | 290 | static void omap_mbox_fini(struct omap_mbox *mbox) |
300 | { | 291 | { |
292 | free_irq(mbox->irq, mbox); | ||
293 | tasklet_kill(&mbox->txq->tasklet); | ||
294 | flush_work(&mbox->rxq->work); | ||
301 | mbox_queue_free(mbox->txq); | 295 | mbox_queue_free(mbox->txq); |
302 | mbox_queue_free(mbox->rxq); | 296 | mbox_queue_free(mbox->rxq); |
303 | 297 | ||
304 | free_irq(mbox->irq, mbox); | 298 | if (mbox->ops->shutdown) { |
305 | 299 | mutex_lock(&mbox_configured_lock); | |
306 | if (unlikely(mbox->ops->shutdown)) { | ||
307 | write_lock(&mboxes_lock); | ||
308 | if (mbox_configured > 0) | 300 | if (mbox_configured > 0) |
309 | mbox_configured--; | 301 | mbox_configured--; |
310 | if (!mbox_configured) | 302 | if (!mbox_configured) |
311 | mbox->ops->shutdown(mbox); | 303 | mbox->ops->shutdown(mbox); |
312 | write_unlock(&mboxes_lock); | 304 | mutex_unlock(&mbox_configured_lock); |
313 | } | ||
314 | } | ||
315 | |||
316 | static struct omap_mbox **find_mboxes(const char *name) | ||
317 | { | ||
318 | struct omap_mbox **p; | ||
319 | |||
320 | for (p = &mboxes; *p; p = &(*p)->next) { | ||
321 | if (strcmp((*p)->name, name) == 0) | ||
322 | break; | ||
323 | } | 305 | } |
324 | |||
325 | return p; | ||
326 | } | 306 | } |
327 | 307 | ||
328 | struct omap_mbox *omap_mbox_get(const char *name) | 308 | struct omap_mbox *omap_mbox_get(const char *name) |
@@ -330,14 +310,15 @@ struct omap_mbox *omap_mbox_get(const char *name) | |||
330 | struct omap_mbox *mbox; | 310 | struct omap_mbox *mbox; |
331 | int ret; | 311 | int ret; |
332 | 312 | ||
333 | read_lock(&mboxes_lock); | 313 | if (!mboxes) |
334 | mbox = *(find_mboxes(name)); | 314 | return ERR_PTR(-EINVAL); |
335 | if (mbox == NULL) { | ||
336 | read_unlock(&mboxes_lock); | ||
337 | return ERR_PTR(-ENOENT); | ||
338 | } | ||
339 | 315 | ||
340 | read_unlock(&mboxes_lock); | 316 | for (mbox = *mboxes; mbox; mbox++) |
317 | if (!strcmp(mbox->name, name)) | ||
318 | break; | ||
319 | |||
320 | if (!mbox) | ||
321 | return ERR_PTR(-ENOENT); | ||
341 | 322 | ||
342 | ret = omap_mbox_startup(mbox); | 323 | ret = omap_mbox_startup(mbox); |
343 | if (ret) | 324 | if (ret) |
@@ -353,70 +334,77 @@ void omap_mbox_put(struct omap_mbox *mbox) | |||
353 | } | 334 | } |
354 | EXPORT_SYMBOL(omap_mbox_put); | 335 | EXPORT_SYMBOL(omap_mbox_put); |
355 | 336 | ||
356 | int omap_mbox_register(struct device *parent, struct omap_mbox *mbox) | 337 | static struct class omap_mbox_class = { .name = "mbox", }; |
338 | |||
339 | int omap_mbox_register(struct device *parent, struct omap_mbox **list) | ||
357 | { | 340 | { |
358 | int ret = 0; | 341 | int ret; |
359 | struct omap_mbox **tmp; | 342 | int i; |
360 | 343 | ||
361 | if (!mbox) | 344 | mboxes = list; |
345 | if (!mboxes) | ||
362 | return -EINVAL; | 346 | return -EINVAL; |
363 | if (mbox->next) | ||
364 | return -EBUSY; | ||
365 | |||
366 | write_lock(&mboxes_lock); | ||
367 | tmp = find_mboxes(mbox->name); | ||
368 | if (*tmp) { | ||
369 | ret = -EBUSY; | ||
370 | write_unlock(&mboxes_lock); | ||
371 | goto err_find; | ||
372 | } | ||
373 | *tmp = mbox; | ||
374 | write_unlock(&mboxes_lock); | ||
375 | 347 | ||
348 | for (i = 0; mboxes[i]; i++) { | ||
349 | struct omap_mbox *mbox = mboxes[i]; | ||
350 | mbox->dev = device_create(&omap_mbox_class, | ||
351 | parent, 0, mbox, "%s", mbox->name); | ||
352 | if (IS_ERR(mbox->dev)) { | ||
353 | ret = PTR_ERR(mbox->dev); | ||
354 | goto err_out; | ||
355 | } | ||
356 | } | ||
376 | return 0; | 357 | return 0; |
377 | 358 | ||
378 | err_find: | 359 | err_out: |
360 | while (i--) | ||
361 | device_unregister(mboxes[i]->dev); | ||
379 | return ret; | 362 | return ret; |
380 | } | 363 | } |
381 | EXPORT_SYMBOL(omap_mbox_register); | 364 | EXPORT_SYMBOL(omap_mbox_register); |
382 | 365 | ||
383 | int omap_mbox_unregister(struct omap_mbox *mbox) | 366 | int omap_mbox_unregister(void) |
384 | { | 367 | { |
385 | struct omap_mbox **tmp; | 368 | int i; |
386 | 369 | ||
387 | write_lock(&mboxes_lock); | 370 | if (!mboxes) |
388 | tmp = &mboxes; | 371 | return -EINVAL; |
389 | while (*tmp) { | ||
390 | if (mbox == *tmp) { | ||
391 | *tmp = mbox->next; | ||
392 | mbox->next = NULL; | ||
393 | write_unlock(&mboxes_lock); | ||
394 | return 0; | ||
395 | } | ||
396 | tmp = &(*tmp)->next; | ||
397 | } | ||
398 | write_unlock(&mboxes_lock); | ||
399 | 372 | ||
400 | return -EINVAL; | 373 | for (i = 0; mboxes[i]; i++) |
374 | device_unregister(mboxes[i]->dev); | ||
375 | mboxes = NULL; | ||
376 | return 0; | ||
401 | } | 377 | } |
402 | EXPORT_SYMBOL(omap_mbox_unregister); | 378 | EXPORT_SYMBOL(omap_mbox_unregister); |
403 | 379 | ||
404 | static int __init omap_mbox_init(void) | 380 | static int __init omap_mbox_init(void) |
405 | { | 381 | { |
382 | int err; | ||
383 | |||
384 | err = class_register(&omap_mbox_class); | ||
385 | if (err) | ||
386 | return err; | ||
387 | |||
406 | mboxd = create_workqueue("mboxd"); | 388 | mboxd = create_workqueue("mboxd"); |
407 | if (!mboxd) | 389 | if (!mboxd) |
408 | return -ENOMEM; | 390 | return -ENOMEM; |
409 | 391 | ||
392 | /* kfifo size sanity check: alignment and minimal size */ | ||
393 | mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t)); | ||
394 | mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(mbox_msg_t)); | ||
395 | |||
410 | return 0; | 396 | return 0; |
411 | } | 397 | } |
412 | module_init(omap_mbox_init); | 398 | subsys_initcall(omap_mbox_init); |
413 | 399 | ||
414 | static void __exit omap_mbox_exit(void) | 400 | static void __exit omap_mbox_exit(void) |
415 | { | 401 | { |
416 | destroy_workqueue(mboxd); | 402 | destroy_workqueue(mboxd); |
403 | class_unregister(&omap_mbox_class); | ||
417 | } | 404 | } |
418 | module_exit(omap_mbox_exit); | 405 | module_exit(omap_mbox_exit); |
419 | 406 | ||
420 | MODULE_LICENSE("GPL v2"); | 407 | MODULE_LICENSE("GPL v2"); |
421 | MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging"); | 408 | MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging"); |
422 | MODULE_AUTHOR("Toshihiro Kobayashi and Hiroshi DOYU"); | 409 | MODULE_AUTHOR("Toshihiro Kobayashi"); |
410 | MODULE_AUTHOR("Hiroshi DOYU"); | ||