18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (c) 2001 Vojtech Pavlik
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/*
78c2ecf20Sopenharmony_ci * EMU10k1 - SB Live / Audigy - gameport driver for Linux
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <asm/io.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/ioport.h>
178c2ecf20Sopenharmony_ci#include <linux/gameport.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EMU10k1 gameport driver");
238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct emu {
268c2ecf20Sopenharmony_ci	struct pci_dev *dev;
278c2ecf20Sopenharmony_ci	struct gameport *gameport;
288c2ecf20Sopenharmony_ci	int io;
298c2ecf20Sopenharmony_ci	int size;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic const struct pci_device_id emu_tbl[] = {
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	{ 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */
358c2ecf20Sopenharmony_ci	{ 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */
368c2ecf20Sopenharmony_ci	{ 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */
378c2ecf20Sopenharmony_ci	{ 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */
388c2ecf20Sopenharmony_ci	{ 0, }
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, emu_tbl);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct emu *emu;
468c2ecf20Sopenharmony_ci	struct gameport *port;
478c2ecf20Sopenharmony_ci	int error;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	emu = kzalloc(sizeof(struct emu), GFP_KERNEL);
508c2ecf20Sopenharmony_ci	port = gameport_allocate_port();
518c2ecf20Sopenharmony_ci	if (!emu || !port) {
528c2ecf20Sopenharmony_ci		printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
538c2ecf20Sopenharmony_ci		error = -ENOMEM;
548c2ecf20Sopenharmony_ci		goto err_out_free;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	error = pci_enable_device(pdev);
588c2ecf20Sopenharmony_ci	if (error)
598c2ecf20Sopenharmony_ci		goto err_out_free;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	emu->io = pci_resource_start(pdev, 0);
628c2ecf20Sopenharmony_ci	emu->size = pci_resource_len(pdev, 0);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	emu->dev = pdev;
658c2ecf20Sopenharmony_ci	emu->gameport = port;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	gameport_set_name(port, "EMU10K1");
688c2ecf20Sopenharmony_ci	gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
698c2ecf20Sopenharmony_ci	port->dev.parent = &pdev->dev;
708c2ecf20Sopenharmony_ci	port->io = emu->io;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (!request_region(emu->io, emu->size, "emu10k1-gp")) {
738c2ecf20Sopenharmony_ci		printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n",
748c2ecf20Sopenharmony_ci			emu->io, emu->io + emu->size - 1);
758c2ecf20Sopenharmony_ci		error = -EBUSY;
768c2ecf20Sopenharmony_ci		goto err_out_disable_dev;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, emu);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	gameport_register_port(port);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci err_out_disable_dev:
868c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
878c2ecf20Sopenharmony_ci err_out_free:
888c2ecf20Sopenharmony_ci	gameport_free_port(port);
898c2ecf20Sopenharmony_ci	kfree(emu);
908c2ecf20Sopenharmony_ci	return error;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void emu_remove(struct pci_dev *pdev)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct emu *emu = pci_get_drvdata(pdev);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	gameport_unregister_port(emu->gameport);
988c2ecf20Sopenharmony_ci	release_region(emu->io, emu->size);
998c2ecf20Sopenharmony_ci	kfree(emu);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic struct pci_driver emu_driver = {
1058c2ecf20Sopenharmony_ci        .name =         "Emu10k1_gameport",
1068c2ecf20Sopenharmony_ci        .id_table =     emu_tbl,
1078c2ecf20Sopenharmony_ci        .probe =        emu_probe,
1088c2ecf20Sopenharmony_ci	.remove =	emu_remove,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cimodule_pci_driver(emu_driver);
112