visorchannel_funcs.c revision d9355f8934a4c54d81a29ae7bd5ae4329c02aa48
1/* visorchannel_funcs.c
2 *
3 * Copyright � 2010 - 2013 UNISYS CORPORATION
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT.  See the GNU General Public License for more
15 * details.
16 */
17
18/*
19 *  This provides Supervisor channel communication primitives, which are
20 *  independent of the mechanism used to access the channel data.  All channel
21 *  data is accessed using the memregion abstraction.  (memregion has both
22 *  a CM2 implementation and a direct memory implementation.)
23 */
24
25#include "globals.h"
26#include "visorchannel.h"
27#include "guidutils.h"
28
29#define MYDRVNAME "visorchannel"
30
31struct VISORCHANNEL_Tag {
32	MEMREGION *memregion;	/* from visor_memregion_create() */
33	CHANNEL_HEADER chan_hdr;
34	GUID guid;
35	ulong size;
36	BOOL needs_lock;
37	spinlock_t insert_lock;
38	spinlock_t remove_lock;
39
40	struct {
41		SIGNAL_QUEUE_HEADER req_queue;
42		SIGNAL_QUEUE_HEADER rsp_queue;
43		SIGNAL_QUEUE_HEADER event_queue;
44		SIGNAL_QUEUE_HEADER ack_queue;
45	} safe_uis_queue;
46};
47
48/* Creates the VISORCHANNEL abstraction for a data area in memory, but does
49 * NOT modify this data area.
50 */
51static VISORCHANNEL *
52visorchannel_create_guts(HOSTADDRESS physaddr, ulong channelBytes,
53			 VISORCHANNEL *parent, ulong off, GUID guid,
54			 BOOL needs_lock)
55{
56	VISORCHANNEL *p = NULL;
57	void *rc = NULL;
58
59	p = kmalloc(sizeof(VISORCHANNEL), GFP_KERNEL|__GFP_NORETRY);
60	if (p == NULL)
61		FAIL("allocation failed", 0);
62	p->memregion = NULL;
63	p->needs_lock = needs_lock;
64	spin_lock_init(&p->insert_lock);
65	spin_lock_init(&p->remove_lock);
66
67	/* prepare chan_hdr (abstraction to read/write channel memory) */
68	if (parent == NULL)
69		p->memregion =
70		    visor_memregion_create(physaddr, sizeof(CHANNEL_HEADER));
71	else
72		p->memregion =
73		    visor_memregion_create_overlapped(parent->memregion,
74						      off,
75						      sizeof(CHANNEL_HEADER));
76	if (p->memregion == NULL)
77		FAIL("visor_memregion_create failed", 0);
78	if (visor_memregion_read(p->memregion, 0, &p->chan_hdr,
79				 sizeof(CHANNEL_HEADER)) < 0)
80		FAIL("visor_memregion_read failed", 0);
81	if (channelBytes == 0)
82		/* we had better be a CLIENT of this channel */
83		channelBytes = (ulong) p->chan_hdr.Size;
84	if (STRUCTSEQUAL(guid, Guid0))
85		/* we had better be a CLIENT of this channel */
86		guid = p->chan_hdr.Type;
87	if (visor_memregion_resize(p->memregion, channelBytes) < 0)
88		FAIL("visor_memregion_resize failed", 0);
89	p->size = channelBytes;
90	p->guid = guid;
91
92	rc = p;
93Away:
94
95	if (rc == NULL) {
96		if (p != NULL) {
97			visorchannel_destroy(p);
98			p = NULL;
99		}
100	}
101	return rc;
102}
103
104VISORCHANNEL *
105visorchannel_create(HOSTADDRESS physaddr, ulong channelBytes, GUID guid)
106{
107	return visorchannel_create_guts(physaddr, channelBytes, NULL, 0, guid,
108					FALSE);
109}
110EXPORT_SYMBOL_GPL(visorchannel_create);
111
112VISORCHANNEL *
113visorchannel_create_with_lock(HOSTADDRESS physaddr, ulong channelBytes,
114			      GUID guid)
115{
116	return visorchannel_create_guts(physaddr, channelBytes, NULL, 0, guid,
117					TRUE);
118}
119EXPORT_SYMBOL_GPL(visorchannel_create_with_lock);
120
121VISORCHANNEL *
122visorchannel_create_overlapped(ulong channelBytes,
123			       VISORCHANNEL *parent, ulong off, GUID guid)
124{
125	return visorchannel_create_guts(0, channelBytes, parent, off, guid,
126					FALSE);
127}
128EXPORT_SYMBOL_GPL(visorchannel_create_overlapped);
129
130VISORCHANNEL *
131visorchannel_create_overlapped_with_lock(ulong channelBytes,
132					 VISORCHANNEL *parent, ulong off,
133					 GUID guid)
134{
135	return visorchannel_create_guts(0, channelBytes, parent, off, guid,
136					TRUE);
137}
138EXPORT_SYMBOL_GPL(visorchannel_create_overlapped_with_lock);
139
140void
141visorchannel_destroy(VISORCHANNEL *channel)
142{
143	if (channel == NULL)
144		return;
145	if (channel->memregion != NULL) {
146		visor_memregion_destroy(channel->memregion);
147		channel->memregion = NULL;
148	}
149	kfree(channel);
150}
151EXPORT_SYMBOL_GPL(visorchannel_destroy);
152
153HOSTADDRESS
154visorchannel_get_physaddr(VISORCHANNEL *channel)
155{
156	return visor_memregion_get_physaddr(channel->memregion);
157}
158EXPORT_SYMBOL_GPL(visorchannel_get_physaddr);
159
160ulong
161visorchannel_get_nbytes(VISORCHANNEL *channel)
162{
163	return channel->size;
164}
165EXPORT_SYMBOL_GPL(visorchannel_get_nbytes);
166
167char *
168visorchannel_GUID_id(GUID *guid, char *s)
169{
170	return GUID_format1(guid, s);
171}
172EXPORT_SYMBOL_GPL(visorchannel_GUID_id);
173
174char *
175visorchannel_id(VISORCHANNEL *channel, char *s)
176{
177	return visorchannel_GUID_id(&channel->guid, s);
178}
179EXPORT_SYMBOL_GPL(visorchannel_id);
180
181char *
182visorchannel_zoneid(VISORCHANNEL *channel, char *s)
183{
184	return visorchannel_GUID_id(&channel->chan_hdr.ZoneGuid, s);
185}
186EXPORT_SYMBOL_GPL(visorchannel_zoneid);
187
188HOSTADDRESS
189visorchannel_get_clientpartition(VISORCHANNEL *channel)
190{
191	return channel->chan_hdr.PartitionHandle;
192}
193EXPORT_SYMBOL_GPL(visorchannel_get_clientpartition);
194
195GUID
196visorchannel_get_GUID(VISORCHANNEL *channel)
197{
198	return channel->guid;
199}
200EXPORT_SYMBOL_GPL(visorchannel_get_GUID);
201
202MEMREGION *
203visorchannel_get_memregion(VISORCHANNEL *channel)
204{
205	return channel->memregion;
206}
207EXPORT_SYMBOL_GPL(visorchannel_get_memregion);
208
209int
210visorchannel_read(VISORCHANNEL *channel, ulong offset,
211		  void *local, ulong nbytes)
212{
213	int rc = visor_memregion_read(channel->memregion, offset,
214				      local, nbytes);
215	if ((rc >= 0) && (offset == 0) && (nbytes >= sizeof(CHANNEL_HEADER)))
216		memcpy(&channel->chan_hdr, local, sizeof(CHANNEL_HEADER));
217	return rc;
218}
219EXPORT_SYMBOL_GPL(visorchannel_read);
220
221int
222visorchannel_write(VISORCHANNEL *channel, ulong offset,
223		   void *local, ulong nbytes)
224{
225	if (offset == 0 && nbytes >= sizeof(CHANNEL_HEADER))
226		memcpy(&channel->chan_hdr, local, sizeof(CHANNEL_HEADER));
227	return visor_memregion_write(channel->memregion, offset, local, nbytes);
228}
229EXPORT_SYMBOL_GPL(visorchannel_write);
230
231int
232visorchannel_clear(VISORCHANNEL *channel, ulong offset, U8 ch, ulong nbytes)
233{
234	int rc = -1;
235	int bufsize = 65536;
236	int written = 0;
237	U8 *buf = vmalloc(bufsize);
238
239	if (buf == NULL) {
240		ERRDRV("%s failed memory allocation", __func__);
241		RETINT(-1);
242	}
243	memset(buf, ch, bufsize);
244	while (nbytes > 0) {
245		ulong thisbytes = bufsize;
246		int x = -1;
247		if (nbytes < thisbytes)
248			thisbytes = nbytes;
249		x = visor_memregion_write(channel->memregion, offset + written,
250					  buf, thisbytes);
251		if (x < 0)
252			RETINT(x);
253		written += thisbytes;
254		nbytes -= thisbytes;
255	}
256	RETINT(0);
257
258Away:
259	if (buf != NULL) {
260		vfree(buf);
261		buf = NULL;
262	}
263	return rc;
264}
265EXPORT_SYMBOL_GPL(visorchannel_clear);
266
267void *
268visorchannel_get_header(VISORCHANNEL *channel)
269{
270	return (void *) &(channel->chan_hdr);
271}
272EXPORT_SYMBOL_GPL(visorchannel_get_header);
273
274/** Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a
275 *  channel header
276 */
277#define SIG_QUEUE_OFFSET(chan_hdr, q) \
278	((chan_hdr)->oChannelSpace + ((q) * sizeof(SIGNAL_QUEUE_HEADER)))
279
280/** Return offset of a specific queue entry (data) from the beginning of a
281 *  channel header
282 */
283#define SIG_DATA_OFFSET(chan_hdr, q, sig_hdr, slot) \
284	(SIG_QUEUE_OFFSET(chan_hdr, q) + (sig_hdr)->oSignalBase + \
285	    ((slot) * (sig_hdr)->SignalSize))
286
287/** Write the contents of a specific field within a SIGNAL_QUEUE_HEADER back
288 *  into host memory
289 */
290#define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD)			\
291	(visor_memregion_write(channel->memregion,			\
292			       SIG_QUEUE_OFFSET(&channel->chan_hdr, queue)+ \
293			       offsetof(SIGNAL_QUEUE_HEADER, FIELD),	\
294			       &((sig_hdr)->FIELD),			\
295			       sizeof((sig_hdr)->FIELD)) >= 0)
296
297static BOOL
298sig_read_header(VISORCHANNEL *channel, U32 queue,
299		SIGNAL_QUEUE_HEADER *sig_hdr)
300{
301	BOOL rc = FALSE;
302
303	if (channel->chan_hdr.oChannelSpace < sizeof(CHANNEL_HEADER))
304		FAIL("oChannelSpace too small", FALSE);
305
306	/* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */
307
308	if (visor_memregion_read(channel->memregion,
309				 SIG_QUEUE_OFFSET(&channel->chan_hdr, queue),
310				 sig_hdr, sizeof(SIGNAL_QUEUE_HEADER)) < 0) {
311		ERRDRV("queue=%d SIG_QUEUE_OFFSET=%d",
312		       queue, (int)SIG_QUEUE_OFFSET(&channel->chan_hdr, queue));
313		FAIL("visor_memregion_read of signal queue failed", FALSE);
314	}
315	rc = TRUE;
316Away:
317	return rc;
318}
319
320static BOOL
321sig_do_data(VISORCHANNEL *channel, U32 queue,
322	    SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data, BOOL is_write)
323{
324	BOOL rc = FALSE;
325	int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue,
326						 sig_hdr, slot);
327	if (is_write) {
328		if (visor_memregion_write(channel->memregion,
329					  signal_data_offset,
330					  data, sig_hdr->SignalSize) < 0)
331			FAIL("visor_memregion_write of signal data failed",
332			     FALSE);
333	} else {
334		if (visor_memregion_read(channel->memregion, signal_data_offset,
335					 data, sig_hdr->SignalSize) < 0)
336			FAIL("visor_memregion_read of signal data failed",
337			     FALSE);
338	}
339	rc = TRUE;
340Away:
341	return rc;
342}
343
344static inline BOOL
345sig_read_data(VISORCHANNEL *channel, U32 queue,
346	      SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data)
347{
348	return sig_do_data(channel, queue, sig_hdr, slot, data, FALSE);
349}
350
351static inline BOOL
352sig_write_data(VISORCHANNEL *channel, U32 queue,
353	       SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data)
354{
355	return sig_do_data(channel, queue, sig_hdr, slot, data, TRUE);
356}
357
358static inline unsigned char
359safe_sig_queue_validate(pSIGNAL_QUEUE_HEADER psafe_sqh,
360			pSIGNAL_QUEUE_HEADER punsafe_sqh,
361			U32 *phead, U32 *ptail)
362{
363	if ((*phead >= psafe_sqh->MaxSignalSlots)
364	    || (*ptail >= psafe_sqh->MaxSignalSlots)) {
365		/* Choose 0 or max, maybe based on current tail value */
366		*phead = 0;
367		*ptail = 0;
368
369		/* Sync with client as necessary */
370		punsafe_sqh->Head = *phead;
371		punsafe_sqh->Tail = *ptail;
372
373		ERRDRV("safe_sig_queue_validate: head = 0x%x, tail = 0x%x, MaxSlots = 0x%x",
374		     *phead, *ptail, psafe_sqh->MaxSignalSlots);
375		return 0;
376	}
377	return 1;
378}				/* end safe_sig_queue_validate */
379
380BOOL
381visorchannel_signalremove(VISORCHANNEL *channel, U32 queue, void *msg)
382{
383	BOOL rc = FALSE;
384	SIGNAL_QUEUE_HEADER sig_hdr;
385
386	if (channel->needs_lock)
387		spin_lock(&channel->remove_lock);
388
389	if (!sig_read_header(channel, queue, &sig_hdr)) {
390		rc = FALSE;
391		goto Away;
392	}
393	if (sig_hdr.Head == sig_hdr.Tail) {
394		rc = FALSE;	/* no signals to remove */
395		goto Away;
396	}
397	sig_hdr.Tail = (sig_hdr.Tail + 1) % sig_hdr.MaxSignalSlots;
398	if (!sig_read_data(channel, queue, &sig_hdr, sig_hdr.Tail, msg))
399		FAIL("sig_read_data failed", FALSE);
400	sig_hdr.NumSignalsReceived++;
401
402	/* For each data field in SIGNAL_QUEUE_HEADER that was modified,
403	 * update host memory.
404	 */
405	MEMORYBARRIER;
406	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, Tail))
407		FAIL("visor_memregion_write of Tail failed", FALSE);
408	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumSignalsReceived))
409		FAIL("visor_memregion_write of NumSignalsReceived failed",
410		     FALSE);
411	rc = TRUE;
412Away:
413	if (channel->needs_lock)
414		spin_unlock(&channel->remove_lock);
415
416	return rc;
417}
418EXPORT_SYMBOL_GPL(visorchannel_signalremove);
419
420BOOL
421visorchannel_signalinsert(VISORCHANNEL *channel, U32 queue, void *msg)
422{
423	BOOL rc = FALSE;
424	SIGNAL_QUEUE_HEADER sig_hdr;
425
426	if (channel->needs_lock)
427		spin_lock(&channel->insert_lock);
428
429	if (!sig_read_header(channel, queue, &sig_hdr)) {
430		rc = FALSE;
431		goto Away;
432	}
433
434	sig_hdr.Head = ((sig_hdr.Head + 1) % sig_hdr.MaxSignalSlots);
435	if (sig_hdr.Head == sig_hdr.Tail) {
436		sig_hdr.NumOverflows++;
437		if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumOverflows))
438			FAIL("visor_memregion_write of NumOverflows failed",
439			     FALSE);
440		rc = FALSE;
441		goto Away;
442	}
443
444	if (!sig_write_data(channel, queue, &sig_hdr, sig_hdr.Head, msg))
445		FAIL("sig_write_data failed", FALSE);
446	sig_hdr.NumSignalsSent++;
447
448	/* For each data field in SIGNAL_QUEUE_HEADER that was modified,
449	 * update host memory.
450	 */
451	MEMORYBARRIER;
452	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, Head))
453		FAIL("visor_memregion_write of Head failed", FALSE);
454	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumSignalsSent))
455		FAIL("visor_memregion_write of NumSignalsSent failed", FALSE);
456	rc = TRUE;
457Away:
458	if (channel->needs_lock)
459		spin_unlock(&channel->insert_lock);
460
461	return rc;
462}
463EXPORT_SYMBOL_GPL(visorchannel_signalinsert);
464
465
466int
467visorchannel_signalqueue_slots_avail(VISORCHANNEL *channel, U32 queue)
468{
469	SIGNAL_QUEUE_HEADER sig_hdr;
470	U32 slots_avail, slots_used;
471	U32 head, tail;
472
473	if (!sig_read_header(channel, queue, &sig_hdr))
474		return 0;
475	head = sig_hdr.Head;
476	tail = sig_hdr.Tail;
477	if (head < tail)
478		head = head + sig_hdr.MaxSignalSlots;
479	slots_used = (head - tail);
480	slots_avail = sig_hdr.MaxSignals - slots_used;
481	return (int) slots_avail;
482}
483EXPORT_SYMBOL_GPL(visorchannel_signalqueue_slots_avail);
484
485int
486visorchannel_signalqueue_max_slots(VISORCHANNEL *channel, U32 queue)
487{
488	SIGNAL_QUEUE_HEADER sig_hdr;
489	if (!sig_read_header(channel, queue, &sig_hdr))
490		return 0;
491	return (int) sig_hdr.MaxSignals;
492}
493EXPORT_SYMBOL_GPL(visorchannel_signalqueue_max_slots);
494
495static void
496sigqueue_debug(SIGNAL_QUEUE_HEADER *q, int which, struct seq_file *seq)
497{
498	seq_printf(seq, "Signal Queue #%d\n", which);
499	seq_printf(seq, "   VersionId          = %lu\n", (ulong) q->VersionId);
500	seq_printf(seq, "   Type               = %lu\n", (ulong) q->Type);
501	seq_printf(seq, "   oSignalBase        = %llu\n",
502		   (long long) q->oSignalBase);
503	seq_printf(seq, "   SignalSize         = %lu\n", (ulong) q->SignalSize);
504	seq_printf(seq, "   MaxSignalSlots     = %lu\n",
505		   (ulong) q->MaxSignalSlots);
506	seq_printf(seq, "   MaxSignals         = %lu\n", (ulong) q->MaxSignals);
507	seq_printf(seq, "   FeatureFlags       = %-16.16Lx\n",
508		   (long long) q->FeatureFlags);
509	seq_printf(seq, "   NumSignalsSent     = %llu\n",
510		   (long long) q->NumSignalsSent);
511	seq_printf(seq, "   NumSignalsReceived = %llu\n",
512		   (long long) q->NumSignalsReceived);
513	seq_printf(seq, "   NumOverflows       = %llu\n",
514		   (long long) q->NumOverflows);
515	seq_printf(seq, "   Head               = %lu\n", (ulong) q->Head);
516	seq_printf(seq, "   Tail               = %lu\n", (ulong) q->Tail);
517}
518
519void
520visorchannel_debug(VISORCHANNEL *channel, int nQueues,
521		   struct seq_file *seq, U32 off)
522{
523	HOSTADDRESS addr = 0;
524	ulong nbytes = 0, nbytes_region = 0;
525	MEMREGION *memregion = NULL;
526	CHANNEL_HEADER hdr;
527	CHANNEL_HEADER *phdr = &hdr;
528	char s[99];
529	int i = 0;
530	int errcode = 0;
531
532	if (channel == NULL) {
533		ERRDRV("%s no channel", __func__);
534		return;
535	}
536	memregion = channel->memregion;
537	if (memregion == NULL) {
538		ERRDRV("%s no memregion", __func__);
539		return;
540	}
541	addr = visor_memregion_get_physaddr(memregion);
542	nbytes_region = visor_memregion_get_nbytes(memregion);
543	errcode = visorchannel_read(channel, off,
544				    phdr, sizeof(CHANNEL_HEADER));
545	if (errcode < 0) {
546		seq_printf(seq,
547			   "Read of channel header failed with errcode=%d)\n",
548			   errcode);
549		if (off == 0) {
550			phdr = &channel->chan_hdr;
551			seq_puts(seq, "(following data may be stale)\n");
552		} else
553			return;
554	}
555	nbytes = (ulong) (phdr->Size);
556	seq_printf(seq, "--- Begin channel @0x%-16.16Lx for 0x%lx bytes (region=0x%lx bytes) ---\n",
557		   addr + off, nbytes, nbytes_region);
558	seq_printf(seq, "Type            = %s\n", GUID_format2(&phdr->Type, s));
559	seq_printf(seq, "ZoneGuid        = %s\n",
560		   GUID_format2(&phdr->ZoneGuid, s));
561	seq_printf(seq, "Signature       = 0x%-16.16Lx\n",
562		   (long long) phdr->Signature);
563	seq_printf(seq, "LegacyState     = %lu\n", (ulong) phdr->LegacyState);
564	seq_printf(seq, "SrvState        = %lu\n", (ulong) phdr->SrvState);
565	seq_printf(seq, "CliStateBoot    = %lu\n", (ulong) phdr->CliStateBoot);
566	seq_printf(seq, "CliStateOS      = %lu\n", (ulong) phdr->CliStateOS);
567	seq_printf(seq, "HeaderSize      = %lu\n", (ulong) phdr->HeaderSize);
568	seq_printf(seq, "Size            = %llu\n", (long long) phdr->Size);
569	seq_printf(seq, "Features        = 0x%-16.16llx\n",
570		   (long long) phdr->Features);
571	seq_printf(seq, "PartitionHandle = 0x%-16.16llx\n",
572		   (long long) phdr->PartitionHandle);
573	seq_printf(seq, "Handle          = 0x%-16.16llx\n",
574		   (long long) phdr->Handle);
575	seq_printf(seq, "VersionId       = %lu\n", (ulong) phdr->VersionId);
576	seq_printf(seq, "oChannelSpace   = %llu\n",
577		   (long long) phdr->oChannelSpace);
578	if ((phdr->oChannelSpace == 0) || (errcode < 0))
579		;
580	else
581		for (i = 0; i < nQueues; i++) {
582			SIGNAL_QUEUE_HEADER q;
583			errcode = visorchannel_read(channel,
584						    off + phdr->oChannelSpace +
585						    (i * sizeof(q)),
586						    &q, sizeof(q));
587			if (errcode < 0) {
588				seq_printf(seq,
589					   "failed to read signal queue #%d from channel @0x%-16.16Lx errcode=%d\n",
590					   i, addr, errcode);
591				continue;
592			}
593			sigqueue_debug(&q, i, seq);
594		}
595	seq_printf(seq, "--- End   channel @0x%-16.16Lx for 0x%lx bytes ---\n",
596		   addr + off, nbytes);
597}
598EXPORT_SYMBOL_GPL(visorchannel_debug);
599
600void
601visorchannel_dump_section(VISORCHANNEL *chan, char *s,
602			  int off, int len, struct seq_file *seq)
603{
604	char *buf, *tbuf, *fmtbuf;
605	int fmtbufsize = 0;
606	int i;
607	int errcode = 0;
608
609	fmtbufsize = 100 * COVQ(len, 16);
610	buf = kmalloc(len, GFP_KERNEL|__GFP_NORETRY);
611	fmtbuf = kmalloc(fmtbufsize, GFP_KERNEL|__GFP_NORETRY);
612	if (buf == NULL || fmtbuf == NULL)
613		goto Away;
614
615	errcode = visorchannel_read(chan, off, buf, len);
616	if (errcode < 0) {
617		ERRDRV("%s failed to read %s from channel errcode=%d",
618		       s, __func__, errcode);
619		goto Away;
620	}
621	seq_printf(seq, "channel %s:\n", s);
622	tbuf = buf;
623	while (len > 0) {
624		i = (len < 16) ? len : 16;
625		hex_dump_to_buffer(tbuf, i, 16, 1, fmtbuf, fmtbufsize, TRUE);
626		seq_printf(seq, "%s\n", fmtbuf);
627		tbuf += 16;
628		len -= 16;
629	}
630
631Away:
632	if (buf != NULL) {
633		kfree(buf);
634		buf = NULL;
635	}
636	if (fmtbuf != NULL) {
637		kfree(fmtbuf);
638		fmtbuf = NULL;
639	}
640}
641EXPORT_SYMBOL_GPL(visorchannel_dump_section);
642