// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles * * Main part * * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria * * If distributed as part of the Linux kernel, this code is licensed under the * terms of the GPL v2. * * Otherwise, the following license terms apply: * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1) Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3) The name of the author may not be used to endorse or promote products * * derived from this software without specific psisusbr written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Author: Thomas Winischhofer <thomas@winischhofer.net> *
*/
staticvoid sisusb_free_buffers(struct sisusb_usb_data *sisusb)
{ int i;
for (i = 0; i < NUMOBUFS; i++) {
kfree(sisusb->obuf[i]);
sisusb->obuf[i] = NULL;
}
kfree(sisusb->ibuf);
sisusb->ibuf = NULL;
}
staticvoid sisusb_free_urbs(struct sisusb_usb_data *sisusb)
{ int i;
for (i = 0; i < NUMOBUFS; i++) {
usb_free_urb(sisusb->sisurbout[i]);
sisusb->sisurbout[i] = NULL;
}
usb_free_urb(sisusb->sisurbin);
sisusb->sisurbin = NULL;
}
/* Level 0: USB transport layer */
/* 1. out-bulks */
/* out-urb management */
/* Return 1 if all free, 0 otherwise */ staticint sisusb_all_free(struct sisusb_usb_data *sisusb)
{ int i;
for (i = 0; i < sisusb->numobufs; i++) {
if (sisusb->urbstatus[i] & SU_URB_BUSY) return 0;
}
return 1;
}
/* Kill all busy URBs */ staticvoid sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
{ int i;
if (sisusb_all_free(sisusb)) return;
for (i = 0; i < sisusb->numobufs; i++) {
if (sisusb->urbstatus[i] & SU_URB_BUSY)
usb_kill_urb(sisusb->sisurbout[i]);
}
}
/* Return 1 if ok, 0 if error (not all complete within timeout) */ staticint sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
{ int timeout = 5 * HZ, i = 1;
wait_event_timeout(sisusb->wait_q, (i = sisusb_all_free(sisusb)),
timeout);
return i;
}
staticint sisusb_outurb_available(struct sisusb_usb_data *sisusb)
{ int i;
for (i = 0; i < sisusb->numobufs; i++) {
if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0) return i;
}
return -1;
}
staticint sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
{ int i, timeout = 5 * HZ;
sisusb->completein = 0;
retval = usb_submit_urb(urb, GFP_KERNEL); if (retval == 0) {
wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout); if (!sisusb->completein) { /* URB timed out... kill it and report error */
usb_kill_urb(urb);
retval = -ETIMEDOUT;
} else { /* URB completed within timeout */
retval = urb->status;
readbytes = urb->actual_length;
}
}
if (actual_length)
*actual_length = readbytes;
return retval;
}
/* Level 1: */
/* Send a bulk message of variable size * * To copy the data from userspace, give pointer to "userbuffer", * to copy from (non-DMA) kernel memory, give "kernbuffer". If * both of these are NULL, it is assumed, that the transfer * buffer "sisusb->obuf[index]" is set up with the data to send. * Index is ignored if either kernbuffer or userbuffer is set. * If async is nonzero, URBs will be sent without waiting for * completion of the previous URB. * * (return 0 on success)
*/
staticint sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, char *kernbuffer, constchar __user *userbuffer, int index,
ssize_t *bytes_written, unsignedint tflags, int async)
{ int result = 0, retry, count = len; int passsize, thispass, transferred_len = 0; int fromuser = (userbuffer != NULL) ? 1 : 0; int fromkern = (kernbuffer != NULL) ? 1 : 0; unsignedint pipe; char *buffer;
/* If we copy data from kernel or userspace, force the * allocation of a buffer/urb. If we have the data in * the transfer buffer[index] already, reuse the buffer/URB * if the length is > buffer size. (So, transmitting * large data amounts directly from the transfer buffer * treats the buffer as a ring buffer. However, we need * to sync in this case.)
*/ if (fromuser || fromkern)
index = -1; elseif (len > sisusb->obufsize)
async = 0;
/* Force new allocation in next iteration */ if (fromuser || fromkern)
index = -1;
} while (count > 0);
if (async) { #ifdef SISUSB_DONTSYNC
(*bytes_written) = len; /* Some URBs/buffers might be busy */ #else
sisusb_wait_all_out_complete(sisusb);
(*bytes_written) = transferred_len; /* All URBs and all buffers are available */ #endif
}
return ((*bytes_written) == len) ? 0 : -EIO;
}
/* Receive a bulk message of variable size * * To copy the data to userspace, give pointer to "userbuffer", * to copy to kernel memory, give "kernbuffer". One of them * MUST be set. (There is no technique for letting the caller * read directly from the ibuf.) *
*/
staticint sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read, unsignedint tflags)
{ int result = 0, retry, count = len; int bufsize, thispass, transferred_len; unsignedint pipe; char *buffer;
staticint sisusb_send_packet(struct sisusb_usb_data *sisusb, int len, struct sisusb_packet *packet)
{ int ret;
ssize_t bytes_transferred = 0;
__le32 tmp;
if (len == 6)
packet->data = 0;
#ifdef SISUSB_DONTSYNC if (!(sisusb_wait_all_out_complete(sisusb))) return 1; #endif
/* Eventually correct endianness */
SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
/* 1. send the packet */
ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
(char *)packet, NULL, 0, &bytes_transferred, 0, 0);
if ((ret == 0) && (len == 6)) {
/* 2. if packet len == 6, it means we read, so wait for 32bit * return value and write it to packet->data
*/
ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
(char *)&tmp, NULL, &bytes_transferred, 0);
packet->data = le32_to_cpu(tmp);
}
return ret;
}
staticint sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, struct sisusb_packet *packet, unsignedint tflags)
{ int ret;
ssize_t bytes_transferred = 0;
__le32 tmp;
if (len == 6)
packet->data = 0;
#ifdef SISUSB_DONTSYNC if (!(sisusb_wait_all_out_complete(sisusb))) return 1; #endif
/* Eventually correct endianness */
SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
/* 1. send the packet */
ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
(char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
if ((ret == 0) && (len == 6)) {
/* 2. if packet len == 6, it means we read, so wait for 32bit * return value and write it to packet->data
*/
ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
(char *)&tmp, NULL, &bytes_transferred, 0);
packet->data = le32_to_cpu(tmp);
}
return ret;
}
/* access video memory and mmio (return 0 on success) */
/* Low level */
/* The following routines assume being used to transfer byte, word, * long etc. * This means that * - the write routines expect "data" in machine endianness format. * The data will be converted to leXX in sisusb_xxx_packet. * - the read routines can expect read data in machine-endianess.
*/
staticint sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
u32 addr, u32 data)
{ struct sisusb_packet packet; int ret = 0;
packet.address = addr & ~3;
switch (addr & 3) { case 0:
packet.header = (type << 6) | 0x0007;
packet.data = data & 0x00ffffff;
ret = sisusb_send_packet(sisusb, 10, &packet); break; case 1:
packet.header = (type << 6) | 0x000e;
packet.data = data << 8;
ret = sisusb_send_packet(sisusb, 10, &packet); break; case 2:
packet.header = (type << 6) | 0x000c;
packet.data = data << 16;
ret = sisusb_send_packet(sisusb, 10, &packet);
packet.header = (type << 6) | 0x0001;
packet.address = (addr & ~3) + 4;
packet.data = (data >> 16) & 0x00ff;
ret |= sisusb_send_packet(sisusb, 10, &packet); break; case 3:
packet.header = (type << 6) | 0x0008;
packet.data = data << 24;
ret = sisusb_send_packet(sisusb, 10, &packet);
packet.header = (type << 6) | 0x0003;
packet.address = (addr & ~3) + 4;
packet.data = (data >> 8) & 0xffff;
ret |= sisusb_send_packet(sisusb, 10, &packet);
}
return ret;
}
staticint sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
u32 addr, u32 data)
{ struct sisusb_packet packet; int ret = 0;
packet.address = addr & ~3;
switch (addr & 3) { case 0:
packet.header = (type << 6) | 0x000f;
packet.data = data;
ret = sisusb_send_packet(sisusb, 10, &packet); break; case 1:
packet.header = (type << 6) | 0x000e;
packet.data = data << 8;
ret = sisusb_send_packet(sisusb, 10, &packet);
packet.header = (type << 6) | 0x0001;
packet.address = (addr & ~3) + 4;
packet.data = data >> 24;
ret |= sisusb_send_packet(sisusb, 10, &packet); break; case 2:
packet.header = (type << 6) | 0x000c;
packet.data = data << 16;
ret = sisusb_send_packet(sisusb, 10, &packet);
packet.header = (type << 6) | 0x0003;
packet.address = (addr & ~3) + 4;
packet.data = data >> 16;
ret |= sisusb_send_packet(sisusb, 10, &packet); break; case 3:
packet.header = (type << 6) | 0x0008;
packet.data = data << 24;
ret = sisusb_send_packet(sisusb, 10, &packet);
packet.header = (type << 6) | 0x0007;
packet.address = (addr & ~3) + 4;
packet.data = data >> 8;
ret |= sisusb_send_packet(sisusb, 10, &packet);
}
return ret;
}
/* The xxx_bulk routines copy a buffer of variable size. They treat the * buffer as chars, therefore lsb/msb has to be corrected if using the * byte/word/long/etc routines for speed-up * * If data is from userland, set "userbuffer" (and clear "kernbuffer"), * if data is in kernel space, set "kernbuffer" (and clear "userbuffer"); * if neither "kernbuffer" nor "userbuffer" are given, it is assumed * that the data already is in the transfer buffer "sisusb->obuf[index]".
*/
staticint sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, char *kernbuffer, int length, constchar __user *userbuffer, int index, ssize_t *bytes_written)
{ struct sisusb_packet packet; int ret = 0; staticint msgcount;
u8 swap8, fromkern = kernbuffer ? 1 : 0;
u16 swap16;
u32 swap32, flag = (length >> 28) & 1;
u8 buf[4];
/* if neither kernbuffer not userbuffer are given, assume * data in obuf
*/ if (!fromkern && !userbuffer)
kernbuffer = sisusb->obuf[index];
(*bytes_written = 0);
length &= 0x00ffffff;
while (length) { switch (length) { case 1: if (userbuffer) { if (get_user(swap8, (u8 __user *)userbuffer)) return -EFAULT;
} else
swap8 = kernbuffer[0];
ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM,
addr, swap8);
if (!ret)
(*bytes_written)++;
return ret;
case 2: if (userbuffer) { if (get_user(swap16, (u16 __user *)userbuffer)) return -EFAULT;
} else
swap16 = *((u16 *)kernbuffer);
ret = sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
addr, swap16);
/* allocate free buffer/urb and clear the buffer */
i = sisusb_alloc_outbuf(sisusb); if (i < 0) return -EBUSY;
memset(sisusb->obuf[i], 0, sisusb->obufsize);
/* We can write a length > buffer size here. The buffer * data will simply be re-used (like a ring-buffer).
*/
ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
/* Free the buffer/urb */
sisusb_free_outbuf(sisusb, i);
return ret;
}
/* Initialize the graphics core (return 0 on success) * This resets the graphics hardware and puts it into * a defined mode (640x480@60Hz)
*/
#define GETREG(r, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) #define SETREG(r, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) #define SETIREG(r, i, d) sisusb_setidxreg(sisusb, r, i, d) #define GETIREG(r, i, d) sisusb_getidxreg(sisusb, r, i, d) #define SETIREGOR(r, i, o) sisusb_setidxregor(sisusb, r, i, o) #define SETIREGAND(r, i, a) sisusb_setidxregand(sisusb, r, i, a) #define SETIREGANDOR(r, i, a, o) sisusb_setidxregandor(sisusb, r, i, a, o) #define READL(a, d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) #define WRITEL(a, d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) #define READB(a, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) #define WRITEB(a, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
staticint sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
{ int ret = 0;
u32 ramptr = SISUSB_PCI_MEMBASE;
u8 tmp1, tmp2, i, j;
ret |= WRITEB(ramptr, 0xaa);
ret |= WRITEB(ramptr + 16, 0x55);
ret |= READB(ramptr, &tmp1);
ret |= READB(ramptr + 16, &tmp2); if ((tmp1 != 0xaa) || (tmp2 != 0x55)) { for (i = 0, j = 16; i < 2; i++, j += 16) {
ret |= GETIREG(SISSR, 0x21, &tmp1);
ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */
ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
ret |= SETIREG(SISSR, 0x21, tmp1);
ret |= WRITEB(ramptr + 16 + j, j);
ret |= READB(ramptr + 16 + j, &tmp1); if (tmp1 == j) {
ret |= WRITEB(ramptr + j, j); break;
}
}
} return ret;
}
staticint sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index, u8 rankno, u8 chab, const u8 dramtype[][5], int bw)
{ int ret = 0, ranksize;
u8 tmp;
*iret = 0;
if ((rankno == 2) && (dramtype[index][0] == 2)) return ret;
ret = SETIREG(SISSR, 0x14, tmp);
ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
*iret = 1;
return ret;
}
staticint sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret,
u32 inc, int testn)
{ int ret = 0, i;
u32 j, tmp;
*iret = 0;
for (i = 0, j = 0; i < testn; i++) {
ret |= WRITEL(sisusb->vrambase + j, j);
j += inc;
}
for (i = 0, j = 0; i < testn; i++) {
ret |= READL(sisusb->vrambase + j, &tmp); if (tmp != j) return ret;
j += inc;
}
*iret = 1; return ret;
}
staticint sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno, int idx, int bw, const u8 rtype[][5])
{ int ret = 0, i, i2ret;
u32 inc;
*iret = 0;
for (i = rankno; i >= 1; i--) {
inc = 1 << (rtype[idx][2] + rtype[idx][1] + rtype[idx][0] +
bw / 64 + i);
ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); if (!i2ret) return ret;
}
for (i = 0; i < 13; i++) {
ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]); for (j = 2; j > 0; j--) {
ret |= sisusb_set_rank(sisusb, &i2ret, i, j, chab,
sdramtype, bw); if (!i2ret) continue;
ret |= sisusb_check_ranks(sisusb, &i2ret, j, i, bw,
sdramtype); if (i2ret) {
*iret = 0; /* ram size found */ return ret;
}
}
}
return ret;
}
staticint sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr)
{ int ret = 0;
u32 address; int i, length, modex, modey, bpp;
/* Enable VGA */
ret = GETREG(SISVGAEN, &tmp8);
ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
/* Enable GPU access to VRAM */
ret |= GETREG(SISMISCR, &tmp8);
ret |= SETREG(SISMISCW, (tmp8 | 0x01));
if (ret) continue;
/* Reset registers */
ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
ret |= SETIREG(SISSR, 0x05, 0x86);
ret |= SETIREGOR(SISSR, 0x20, 0x01);
ret |= SETREG(SISMISCW, 0x67);
for (i = 0x06; i <= 0x1f; i++)
ret |= SETIREG(SISSR, i, 0x00);
for (i = 0x21; i <= 0x27; i++)
ret |= SETIREG(SISSR, i, 0x00);
for (i = 0x31; i <= 0x3d; i++)
ret |= SETIREG(SISSR, i, 0x00);
for (i = 0x12; i <= 0x1b; i++)
ret |= SETIREG(SISSR, i, 0x00);
for (i = 0x79; i <= 0x7c; i++)
ret |= SETIREG(SISCR, i, 0x00);
if (ret) continue;
ret |= SETIREG(SISCR, 0x63, 0x80);
ret |= GETIREG(SISSR, 0x3a, &ramtype);
ramtype &= 0x03;
ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
ret |= SETIREG(SISSR, 0x07, 0x18);
ret |= SETIREG(SISSR, 0x11, 0x0f);
if (ret) continue;
for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
ret |= SETIREG(SISSR, i,
ramtypetable1[(j*4) + ramtype]);
} for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
ret |= SETIREG(SISCR, i,
ramtypetable2[(j*4) + ramtype]);
}
ret |= SETIREG(SISCR, 0x49, 0xaa);
ret |= SETIREG(SISSR, 0x1f, 0x00);
ret |= SETIREG(SISSR, 0x20, 0xa0);
ret |= SETIREG(SISSR, 0x23, 0xf6);
ret |= SETIREG(SISSR, 0x24, 0x0d);
ret |= SETIREG(SISSR, 0x25, 0x33);
ret |= SETIREG(SISSR, 0x11, 0x0f);
ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
if (ret) continue;
ret |= SETIREG(SISPART1, 0x00, 0x00);
ret |= GETIREG(SISSR, 0x13, &tmp8);
tmp8 >>= 4;
ret |= SETIREG(SISPART1, 0x02, 0x00);
ret |= SETIREG(SISPART1, 0x2e, 0x08);
/* Init BAR 0 (VRAM) */
ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
tmp32 &= 0x0f;
tmp32 |= SISUSB_PCI_MEMBASE;
ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
/* Init BAR 1 (MMIO) */
ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
tmp32 &= 0x0f;
tmp32 |= SISUSB_PCI_MMIOBASE;
ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
/* Init BAR 2 (i/o ports) */
ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
tmp32 &= 0x0f;
tmp32 |= SISUSB_PCI_IOPORTBASE;
ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
/* Enable memory and i/o access */
ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
tmp32 |= 0x3;
ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
if (ret == 0) { /* Some further magic */
packet.header = 0x001f;
packet.address = 0x00000050;
packet.data = 0x000000ff;
ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
}
return ret;
}
/* Initialize the graphics device (return 0 on success) * This initializes the net2280 as well as the PCI registers * of the graphics board.
*/
staticint sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
{ int ret = 0, test = 0;
u32 tmp32;
if (sisusb->devinit == 1) { /* Read PCI BARs and see if they have been set up */
ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); if (ret) return ret;
if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE)
test++;
ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); if (ret) return ret;
if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE)
test++;
ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); if (ret) return ret;
if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE)
test++;
}
/* No? So reset the device */ if ((sisusb->devinit == 0) || (test != 3)) {
ret |= sisusb_do_init_gfxdevice(sisusb);
if (ret == 0)
sisusb->devinit = 1;
}
if (sisusb->devinit) { /* Initialize the graphics core */ if (sisusb_init_gfxcore(sisusb) == 0) {
sisusb->gfxinit = 1;
sisusb_get_ramconfig(sisusb);
sisusb_set_default_mode(sisusb, 1);
ret |= sisusb_setup_screen(sisusb, 1, initscreen);
}
}
sisusb = file->private_data; if (!sisusb) return -ENODEV;
mutex_lock(&sisusb->lock);
if (sisusb->present) { /* Wait for all URBs to finish if device still present */ if (!sisusb_wait_all_out_complete(sisusb))
sisusb_kill_all_busy(sisusb);
}
sisusb->isopen = 0;
file->private_data = NULL;
mutex_unlock(&sisusb->lock);
/* decrement the usage count on our device */
kref_put(&sisusb->kref, sisusb_delete);
/* Read i/o ports * Byte, word and long(32) can be read. As this * emulates inX instructions, the data returned is * in machine-endianness.
*/ switch (count) { case 1: if (sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO,
address, &buf8))
errno = -EIO; elseif (put_user(buf8, (u8 __user *)buffer))
errno = -EFAULT; else
bytes_read = 1;
/* Read video ram * Remember: Data delivered is never endian-corrected
*/
errno = sisusb_read_mem_bulk(sisusb, address,
NULL, count, buffer, &bytes_read);
/* Write i/o ports * Byte, word and long(32) can be written. As this * emulates outX instructions, the data is expected * in machine-endianness.
*/ switch (count) { case 1: if (get_user(buf8, (u8 __user *)buffer))
errno = -EFAULT; elseif (sisusb_write_memio_byte(sisusb,
SISUSB_TYPE_IO, address, buf8))
errno = -EIO; else
bytes_written = 1;
/* Write video ram. * Buffer is copied 1:1, therefore, on big-endian * machines, the data must be swapped by userland * in advance (if applicable; no swapping in 8bpp * mode or if YUV data is being transferred).
*/
errno = sisusb_write_mem_bulk(sisusb, address, NULL,
count, buffer, 0, &bytes_written);
/* Write MMIO. * Buffer is copied 1:1, therefore, on big-endian * machines, the data must be swapped by userland * in advance.
*/
errno = sisusb_write_mem_bulk(sisusb, address, NULL,
count, buffer, 0, &bytes_written);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.