+
+enum parport_pc_pci_cards {
+ titan_110l = 0,
+ titan_210l,
+ netmos_9xx5_combo,
+ netmos_9855,
+ netmos_9855_2p,
+ netmos_9900,
+ netmos_9900_2p,
+ netmos_99xx_1p,
+ avlab_1s1p,
+ avlab_1s2p,
+ avlab_2s1p,
+ siig_1s1p_10x,
+ siig_2s1p_10x,
+ siig_2p1s_20x,
+ siig_1s1p_20x,
+ siig_2s1p_20x,
+ timedia_4078a,
+ timedia_4079h,
+ timedia_4085h,
+ timedia_4088a,
+ timedia_4089a,
+ timedia_4095a,
+ timedia_4096a,
+ timedia_4078u,
+ timedia_4079a,
+ timedia_4085u,
+ timedia_4079r,
+ timedia_4079s,
+ timedia_4079d,
+ timedia_4079e,
+ timedia_4079f,
+ timedia_9079a,
+ timedia_9079b,
+ timedia_9079c,
+ wch_ch353_2s1p,
+ sunix_2s1p,
+};
+
+/* each element directly indexed from enum list, above */
+struct parport_pc_pci {
+ int numports;
+ struct { /* BAR (base address registers) numbers in the config
+ space header */
+ int lo;
+ int hi; /* -1 if not there, >6 for offset-method (max
+ BAR is 6) */
+ } addr[4];
+
+ /* If set, this is called immediately after pci_enable_device.
+ * If it returns non-zero, no probing will take place and the
+ * ports will not be used. */
+ int (*preinit_hook) (struct pci_dev *pdev, struct parport_pc_pci *card,
+ int autoirq, int autodma);
+
+ /* If set, this is called after probing for ports. If 'failed'
+ * is non-zero we couldn't use any of the ports. */
+ void (*postinit_hook) (struct pci_dev *pdev,
+ struct parport_pc_pci *card, int failed);
+};
+
+static int netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *par,
+ int autoirq, int autodma)
+{
+ /* the rule described below doesn't hold for this device */
+ if (dev->device == PCI_DEVICE_ID_NETMOS_9835 &&
+ dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
+ dev->subsystem_device == 0x0299)
+ return -ENODEV;
+
+ if (dev->device == PCI_DEVICE_ID_NETMOS_9912) {
+ par->numports = 1;
+ } else {
+ /*
+ * Netmos uses the subdevice ID to indicate the number of parallel
+ * and serial ports. The form is 0x00PS, where is the number of
+ * parallel ports and is the number of serial ports.
+ */
+ par->numports = (dev->subsystem_device & 0xf0) >> 4;
+ if (par->numports > ARRAY_SIZE(par->addr))
+ par->numports = ARRAY_SIZE(par->addr);
+ }
+
+ return 0;
+}
+
+static struct parport_pc_pci cards[] = {
+ /* titan_110l */ { 1, { { 3, -1 }, } },
+ /* titan_210l */ { 1, { { 3, -1 }, } },
+ /* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init },
+ /* netmos_9855 */ { 1, { { 0, -1 }, }, netmos_parallel_init },
+ /* netmos_9855_2p */ { 2, { { 0, -1 }, { 2, -1 }, } },
+ /* netmos_9900 */ {1, { { 3, 4 }, }, netmos_parallel_init },
+ /* netmos_9900_2p */ {2, { { 0, 1 }, { 3, 4 }, } },
+ /* netmos_99xx_1p */ {1, { { 0, 1 }, } },
+ /* avlab_1s1p */ { 1, { { 1, 2}, } },
+ /* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} },
+ /* avlab_2s1p */ { 1, { { 2, 3}, } },
+ /* siig_1s1p_10x */ { 1, { { 3, 4 }, } },
+ /* siig_2s1p_10x */ { 1, { { 4, 5 }, } },
+ /* siig_2p1s_20x */ { 2, { { 1, 2 }, { 3, 4 }, } },
+ /* siig_1s1p_20x */ { 1, { { 1, 2 }, } },
+ /* siig_2s1p_20x */ { 1, { { 2, 3 }, } },
+ /* timedia_4078a */ { 1, { { 2, -1 }, } },
+ /* timedia_4079h */ { 1, { { 2, 3 }, } },
+ /* timedia_4085h */ { 2, { { 2, -1 }, { 4, -1 }, } },
+ /* timedia_4088a */ { 2, { { 2, 3 }, { 4, 5 }, } },
+ /* timedia_4089a */ { 2, { { 2, 3 }, { 4, 5 }, } },
+ /* timedia_4095a */ { 2, { { 2, 3 }, { 4, 5 }, } },
+ /* timedia_4096a */ { 2, { { 2, 3 }, { 4, 5 }, } },
+ /* timedia_4078u */ { 1, { { 2, -1 }, } },
+ /* timedia_4079a */ { 1, { { 2, 3 }, } },
+ /* timedia_4085u */ { 2, { { 2, -1 }, { 4, -1 }, } },
+ /* timedia_4079r */ { 1, { { 2, 3 }, } },
+ /* timedia_4079s */ { 1, { { 2, 3 }, } },
+ /* timedia_4079d */ { 1, { { 2, 3 }, } },
+ /* timedia_4079e */ { 1, { { 2, 3 }, } },
+ /* timedia_4079f */ { 1, { { 2, 3 }, } },
+ /* timedia_9079a */ { 1, { { 2, 3 }, } },
+ /* timedia_9079b */ { 1, { { 2, 3 }, } },
+ /* timedia_9079c */ { 1, { { 2, 3 }, } },
+ /* wch_ch353_2s1p*/ { 1, { { 2, -1}, } },
+ /* sunix_2s1p */ { 1, { { 3, -1 }, } },
+};
+
+#define PCI_VENDOR_ID_SUNIX 0x1fd4
+#define PCI_DEVICE_ID_SUNIX_1999 0x1999
+
+static struct pci_device_id parport_serial_pci_tbl[] = {
+ /* PCI cards */
+ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l },
+ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
+ 0x1000, 0x0020, 0, 0, netmos_9855_2p },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
+ 0x1000, 0x0022, 0, 0, netmos_9855_2p },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+ 0xA000, 0x3011, 0, 0, netmos_9900 },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+ 0xA000, 0x3012, 0, 0, netmos_9900 },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+ 0xA000, 0x3020, 0, 0, netmos_9900_2p },
+ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
+ 0xA000, 0x2000, 0, 0, netmos_99xx_1p },
+ /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
+ { PCI_VENDOR_ID_AFAVLAB, 0x2110,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2111,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2112,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2140,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2141,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2142,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2160,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2161,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
+ { PCI_VENDOR_ID_AFAVLAB, 0x2162,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
+ /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
+ { 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a },
+ { 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h },
+ { 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h },
+ { 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a },
+ { 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a },
+ { 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a },
+ { 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a },
+ { 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u },
+ { 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a },
+ { 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u },
+ { 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r },
+ { 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s },
+ { 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d },
+ { 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e },
+ { 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f },
+ { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
+ { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
+ { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
+
+ /* WCH CARDS */
+ { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
+
+ /*
+ * More SUNIX variations. At least one of these has part number
+ * '5079A but subdevice 0x102. That board reports 0x0708 as
+ * its PCI Class.
+ */
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
+ 0x0102, 0, 0, sunix_2s1p },
+
+ { 0, } /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
+
+/*
+ * This table describes the serial "geometry" of these boards. Any
+ * quirks for these can be found in drivers/serial/8250_pci.c
+ *
+ * Cards not tested are marked n/t
+ * If you have one of these cards and it works for you, please tell me..
+ */
+static struct pciserial_board pci_parport_serial_boards[] = {
+ [titan_110l] = {
+ .flags = FL_BASE1 | FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [titan_210l] = {
+ .flags = FL_BASE1 | FL_BASE_BARS,
+ .num_ports = 2,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [netmos_9xx5_combo] = {
+ .flags = FL_BASE0 | FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [netmos_9855] = {
+ .flags = FL_BASE2 | FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [netmos_9855_2p] = {
+ .flags = FL_BASE4 | FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [netmos_9900] = { /* n/t */
+ .flags = FL_BASE0 | FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [netmos_9900_2p] = { /* parallel only */ /* n/t */
+ .flags = FL_BASE0,
+ .num_ports = 0,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [netmos_99xx_1p] = { /* parallel only */ /* n/t */
+ .flags = FL_BASE0,
+ .num_ports = 0,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [avlab_1s1p] = { /* n/t */
+ .flags = FL_BASE0 | FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [avlab_1s2p] = { /* n/t */
+ .flags = FL_BASE0 | FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [avlab_2s1p] = { /* n/t */
+ .flags = FL_BASE0 | FL_BASE_BARS,
+ .num_ports = 2,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [siig_1s1p_10x] = {
+ .flags = FL_BASE2,
+ .num_ports = 1,
+ .base_baud = 460800,
+ .uart_offset = 8,
+ },
+ [siig_2s1p_10x] = {
+ .flags = FL_BASE2,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [siig_2p1s_20x] = {
+ .flags = FL_BASE0,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [siig_1s1p_20x] = {
+ .flags = FL_BASE0,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [siig_2s1p_20x] = {
+ .flags = FL_BASE0,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4078a] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4079h] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4085h] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4088a] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4089a] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4095a] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4096a] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4078u] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4079a] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4085u] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4079r] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4079s] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4079d] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4079e] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_4079f] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_9079a] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_9079b] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [timedia_9079c] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+ [wch_ch353_2s1p] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 2,
+ .base_baud = 115200,
+ .uart_offset = 8,
+ },
+ [sunix_2s1p] = {
+ .flags = FL_BASE0|FL_BASE_BARS,
+ .num_ports = 2,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
+};
+
+struct parport_serial_private {
+ struct serial_private *serial;
+ int num_par;
+ struct parport *port[PARPORT_MAX];
+ struct parport_pc_pci par;
+};
+
+/* Register the serial port(s) of a PCI card. */
+static int serial_register(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct parport_serial_private *priv = pci_get_drvdata (dev);
+ struct pciserial_board *board;
+ struct serial_private *serial;
+
+ board = &pci_parport_serial_boards[id->driver_data];
+
+ if (board->num_ports == 0)
+ return 0;
+
+ serial = pciserial_init_ports(dev, board);
+
+ if (IS_ERR(serial))
+ return PTR_ERR(serial);
+
+ priv->serial = serial;
+ return 0;
+}
+
+/* Register the parallel port(s) of a PCI card. */
+static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct parport_pc_pci *card;
+ struct parport_serial_private *priv = pci_get_drvdata (dev);
+ int n, success = 0;
+
+ priv->par = cards[id->driver_data];
+ card = &priv->par;
+ if (card->preinit_hook &&
+ card->preinit_hook (dev, card, PARPORT_IRQ_NONE, PARPORT_DMA_NONE))
+ return -ENODEV;
+
+ for (n = 0; n < card->numports; n++) {
+ struct parport *port;
+ int lo = card->addr[n].lo;
+ int hi = card->addr[n].hi;
+ unsigned long io_lo, io_hi;
+ int irq;
+
+ if (priv->num_par == ARRAY_SIZE (priv->port)) {
+ printk (KERN_WARNING
+ "parport_serial: %s: only %zu parallel ports "
+ "supported (%d reported)\n", pci_name (dev),
+ ARRAY_SIZE(priv->port), card->numports);
+ break;
+ }
+
+ io_lo = pci_resource_start (dev, lo);
+ io_hi = 0;
+ if ((hi >= 0) && (hi <= 6))
+ io_hi = pci_resource_start (dev, hi);
+ else if (hi > 6)
+ io_lo += hi; /* Reinterpret the meaning of
+ "hi" as an offset (see SYBA
+ def.) */
+ /* TODO: test if sharing interrupts works */
+ irq = dev->irq;
+ if (irq == IRQ_NONE) {
+ dev_dbg(&dev->dev,
+ "PCI parallel port detected: I/O at %#lx(%#lx)\n",
+ io_lo, io_hi);
+ irq = PARPORT_IRQ_NONE;
+ } else {
+ dev_dbg(&dev->dev,
+ "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
+ io_lo, io_hi, irq);
+ }
+ port = parport_pc_probe_port (io_lo, io_hi, irq,
+ PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
+ if (port) {
+ priv->port[priv->num_par++] = port;
+ success = 1;
+ }
+ }
+
+ if (card->postinit_hook)
+ card->postinit_hook (dev, card, !success);
+
+ return 0;
+}
+
+static int parport_serial_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct parport_serial_private *priv;
+ int err;
+
+ priv = kzalloc (sizeof *priv, GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ pci_set_drvdata (dev, priv);
+
+ err = pci_enable_device (dev);
+ if (err) {
+ pci_set_drvdata (dev, NULL);
+ kfree (priv);
+ return err;
+ }
+
+ if (parport_register (dev, id)) {
+ pci_set_drvdata (dev, NULL);
+ kfree (priv);
+ return -ENODEV;
+ }
+
+ if (serial_register (dev, id)) {
+ int i;
+ for (i = 0; i < priv->num_par; i++)
+ parport_pc_unregister_port (priv->port[i]);
+ pci_set_drvdata (dev, NULL);
+ kfree (priv);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void parport_serial_pci_remove(struct pci_dev *dev)
+{
+ struct parport_serial_private *priv = pci_get_drvdata (dev);
+ int i;
+
+ pci_set_drvdata(dev, NULL);
+
+ // Serial ports
+ if (priv->serial)
+ pciserial_remove_ports(priv->serial);
+
+ // Parallel ports
+ for (i = 0; i < priv->num_par; i++)
+ parport_pc_unregister_port (priv->port[i]);
+
+ kfree (priv);
+ return;
+}
+
+#ifdef CONFIG_PM
+static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ struct parport_serial_private *priv = pci_get_drvdata(dev);
+
+ if (priv->serial)
+ pciserial_suspend_ports(priv->serial);
+
+ /* FIXME: What about parport? */
+
+ pci_save_state(dev);
+ pci_set_power_state(dev, pci_choose_state(dev, state));
+ return 0;
+}
+
+static int parport_serial_pci_resume(struct pci_dev *dev)
+{
+ struct parport_serial_private *priv = pci_get_drvdata(dev);
+ int err;
+
+ pci_set_power_state(dev, PCI_D0);
+ pci_restore_state(dev);
+
+ /*
+ * The device may have been disabled. Re-enable it.
+ */
+ err = pci_enable_device(dev);
+ if (err) {
+ printk(KERN_ERR "parport_serial: %s: error enabling "
+ "device for resume (%d)\n", pci_name(dev), err);
+ return err;
+ }
+
+ if (priv->serial)
+ pciserial_resume_ports(priv->serial);
+
+ /* FIXME: What about parport? */
+
+ return 0;
+}
+#endif
+
+static struct pci_driver parport_serial_pci_driver = {
+ .name = "parport_serial",
+ .id_table = parport_serial_pci_tbl,
+ .probe = parport_serial_pci_probe,
+ .remove = parport_serial_pci_remove,
+#ifdef CONFIG_PM
+ .suspend = parport_serial_pci_suspend,
+ .resume = parport_serial_pci_resume,
+#endif
+};
+
+
+static int __init parport_serial_init (void)
+{
+ return pci_register_driver (&parport_serial_pci_driver);
+}
+
+static void __exit parport_serial_exit (void)
+{
+ pci_unregister_driver (&parport_serial_pci_driver);
+ return;
+}
+
+MODULE_AUTHOR("Tim Waugh ");
+MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
+MODULE_LICENSE("GPL");
+
+module_init(parport_serial_init);
+module_exit(parport_serial_exit);
diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c
new file mode 100644
index 00000000..dffd6d0b
--- /dev/null
+++ b/drivers/parport/parport_sunbpp.c
@@ -0,0 +1,378 @@
+/* parport_sunbpp.c: Parallel-port routines for SBUS
+ *
+ * Author: Derrick J. Brashear
+ *
+ * based on work by:
+ * Phil Blundell
+ * Tim Waugh
+ * Jose Renau
+ * David Campbell
+ * Grant Guenther
+ * Eddie C. Dost
+ * Stephen Williams (steve@icarus.com)
+ * Gus Baldauf (gbaldauf@ix.netcom.com)
+ * Peter Zaitcev
+ * Tom Dyas
+ *
+ * Updated to new SBUS device framework: David S. Miller
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include /* OpenProm Library */
+#include /* BPP uses LSI 64854 for DMA */
+#include
+#include
+
+#undef __SUNBPP_DEBUG
+#ifdef __SUNBPP_DEBUG
+#define dprintk(x) printk x
+#else
+#define dprintk(x)
+#endif
+
+static void parport_sunbpp_disable_irq(struct parport *p)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+ u32 tmp;
+
+ tmp = sbus_readl(®s->p_csr);
+ tmp &= ~DMA_INT_ENAB;
+ sbus_writel(tmp, ®s->p_csr);
+}
+
+static void parport_sunbpp_enable_irq(struct parport *p)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+ u32 tmp;
+
+ tmp = sbus_readl(®s->p_csr);
+ tmp |= DMA_INT_ENAB;
+ sbus_writel(tmp, ®s->p_csr);
+}
+
+static void parport_sunbpp_write_data(struct parport *p, unsigned char d)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+
+ sbus_writeb(d, ®s->p_dr);
+ dprintk((KERN_DEBUG "wrote 0x%x\n", d));
+}
+
+static unsigned char parport_sunbpp_read_data(struct parport *p)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+
+ return sbus_readb(®s->p_dr);
+}
+
+static unsigned char status_sunbpp_to_pc(struct parport *p)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+ unsigned char bits = 0;
+ unsigned char value_tcr = sbus_readb(®s->p_tcr);
+ unsigned char value_ir = sbus_readb(®s->p_ir);
+
+ if (!(value_ir & P_IR_ERR))
+ bits |= PARPORT_STATUS_ERROR;
+ if (!(value_ir & P_IR_SLCT))
+ bits |= PARPORT_STATUS_SELECT;
+ if (!(value_ir & P_IR_PE))
+ bits |= PARPORT_STATUS_PAPEROUT;
+ if (value_tcr & P_TCR_ACK)
+ bits |= PARPORT_STATUS_ACK;
+ if (!(value_tcr & P_TCR_BUSY))
+ bits |= PARPORT_STATUS_BUSY;
+
+ dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n", value_tcr, value_ir));
+ dprintk((KERN_DEBUG "read status 0x%x\n", bits));
+ return bits;
+}
+
+static unsigned char control_sunbpp_to_pc(struct parport *p)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+ unsigned char bits = 0;
+ unsigned char value_tcr = sbus_readb(®s->p_tcr);
+ unsigned char value_or = sbus_readb(®s->p_or);
+
+ if (!(value_tcr & P_TCR_DS))
+ bits |= PARPORT_CONTROL_STROBE;
+ if (!(value_or & P_OR_AFXN))
+ bits |= PARPORT_CONTROL_AUTOFD;
+ if (!(value_or & P_OR_INIT))
+ bits |= PARPORT_CONTROL_INIT;
+ if (value_or & P_OR_SLCT_IN)
+ bits |= PARPORT_CONTROL_SELECT;
+
+ dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n", value_tcr, value_or));
+ dprintk((KERN_DEBUG "read control 0x%x\n", bits));
+ return bits;
+}
+
+static unsigned char parport_sunbpp_read_control(struct parport *p)
+{
+ return control_sunbpp_to_pc(p);
+}
+
+static unsigned char parport_sunbpp_frob_control(struct parport *p,
+ unsigned char mask,
+ unsigned char val)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+ unsigned char value_tcr = sbus_readb(®s->p_tcr);
+ unsigned char value_or = sbus_readb(®s->p_or);
+
+ dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n",
+ value_tcr, value_or));
+ if (mask & PARPORT_CONTROL_STROBE) {
+ if (val & PARPORT_CONTROL_STROBE) {
+ value_tcr &= ~P_TCR_DS;
+ } else {
+ value_tcr |= P_TCR_DS;
+ }
+ }
+ if (mask & PARPORT_CONTROL_AUTOFD) {
+ if (val & PARPORT_CONTROL_AUTOFD) {
+ value_or &= ~P_OR_AFXN;
+ } else {
+ value_or |= P_OR_AFXN;
+ }
+ }
+ if (mask & PARPORT_CONTROL_INIT) {
+ if (val & PARPORT_CONTROL_INIT) {
+ value_or &= ~P_OR_INIT;
+ } else {
+ value_or |= P_OR_INIT;
+ }
+ }
+ if (mask & PARPORT_CONTROL_SELECT) {
+ if (val & PARPORT_CONTROL_SELECT) {
+ value_or |= P_OR_SLCT_IN;
+ } else {
+ value_or &= ~P_OR_SLCT_IN;
+ }
+ }
+
+ sbus_writeb(value_or, ®s->p_or);
+ sbus_writeb(value_tcr, ®s->p_tcr);
+ dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n",
+ value_tcr, value_or));
+ return parport_sunbpp_read_control(p);
+}
+
+static void parport_sunbpp_write_control(struct parport *p, unsigned char d)
+{
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
+
+ parport_sunbpp_frob_control (p, wm, d & wm);
+}
+
+static unsigned char parport_sunbpp_read_status(struct parport *p)
+{
+ return status_sunbpp_to_pc(p);
+}
+
+static void parport_sunbpp_data_forward (struct parport *p)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+ unsigned char value_tcr = sbus_readb(®s->p_tcr);
+
+ dprintk((KERN_DEBUG "forward\n"));
+ value_tcr &= ~P_TCR_DIR;
+ sbus_writeb(value_tcr, ®s->p_tcr);
+}
+
+static void parport_sunbpp_data_reverse (struct parport *p)
+{
+ struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
+ u8 val = sbus_readb(®s->p_tcr);
+
+ dprintk((KERN_DEBUG "reverse\n"));
+ val |= P_TCR_DIR;
+ sbus_writeb(val, ®s->p_tcr);
+}
+
+static void parport_sunbpp_init_state(struct pardevice *dev, struct parport_state *s)
+{
+ s->u.pc.ctr = 0xc;
+ s->u.pc.ecr = 0x0;
+}
+
+static void parport_sunbpp_save_state(struct parport *p, struct parport_state *s)
+{
+ s->u.pc.ctr = parport_sunbpp_read_control(p);
+}
+
+static void parport_sunbpp_restore_state(struct parport *p, struct parport_state *s)
+{
+ parport_sunbpp_write_control(p, s->u.pc.ctr);
+}
+
+static struct parport_operations parport_sunbpp_ops =
+{
+ .write_data = parport_sunbpp_write_data,
+ .read_data = parport_sunbpp_read_data,
+
+ .write_control = parport_sunbpp_write_control,
+ .read_control = parport_sunbpp_read_control,
+ .frob_control = parport_sunbpp_frob_control,
+
+ .read_status = parport_sunbpp_read_status,
+
+ .enable_irq = parport_sunbpp_enable_irq,
+ .disable_irq = parport_sunbpp_disable_irq,
+
+ .data_forward = parport_sunbpp_data_forward,
+ .data_reverse = parport_sunbpp_data_reverse,
+
+ .init_state = parport_sunbpp_init_state,
+ .save_state = parport_sunbpp_save_state,
+ .restore_state = parport_sunbpp_restore_state,
+
+ .epp_write_data = parport_ieee1284_epp_write_data,
+ .epp_read_data = parport_ieee1284_epp_read_data,
+ .epp_write_addr = parport_ieee1284_epp_write_addr,
+ .epp_read_addr = parport_ieee1284_epp_read_addr,
+
+ .ecp_write_data = parport_ieee1284_ecp_write_data,
+ .ecp_read_data = parport_ieee1284_ecp_read_data,
+ .ecp_write_addr = parport_ieee1284_ecp_write_addr,
+
+ .compat_write_data = parport_ieee1284_write_compat,
+ .nibble_read_data = parport_ieee1284_read_nibble,
+ .byte_read_data = parport_ieee1284_read_byte,
+
+ .owner = THIS_MODULE,
+};
+
+static int bpp_probe(struct platform_device *op)
+{
+ struct parport_operations *ops;
+ struct bpp_regs __iomem *regs;
+ int irq, dma, err = 0, size;
+ unsigned char value_tcr;
+ void __iomem *base;
+ struct parport *p;
+
+ irq = op->archdata.irqs[0];
+ base = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]),
+ "sunbpp");
+ if (!base)
+ return -ENODEV;
+
+ size = resource_size(&op->resource[0]);
+ dma = PARPORT_DMA_NONE;
+
+ ops = kmemdup(&parport_sunbpp_ops, sizeof(struct parport_operations),
+ GFP_KERNEL);
+ if (!ops)
+ goto out_unmap;
+
+ dprintk(("register_port\n"));
+ if (!(p = parport_register_port((unsigned long)base, irq, dma, ops)))
+ goto out_free_ops;
+
+ p->size = size;
+ p->dev = &op->dev;
+
+ if ((err = request_irq(p->irq, parport_irq_handler,
+ IRQF_SHARED, p->name, p)) != 0) {
+ goto out_put_port;
+ }
+
+ parport_sunbpp_enable_irq(p);
+
+ regs = (struct bpp_regs __iomem *)p->base;
+
+ value_tcr = sbus_readb(®s->p_tcr);
+ value_tcr &= ~P_TCR_DIR;
+ sbus_writeb(value_tcr, ®s->p_tcr);
+
+ printk(KERN_INFO "%s: sunbpp at 0x%lx\n", p->name, p->base);
+
+ dev_set_drvdata(&op->dev, p);
+
+ parport_announce_port(p);
+
+ return 0;
+
+out_put_port:
+ parport_put_port(p);
+
+out_free_ops:
+ kfree(ops);
+
+out_unmap:
+ of_iounmap(&op->resource[0], base, size);
+
+ return err;
+}
+
+static int bpp_remove(struct platform_device *op)
+{
+ struct parport *p = dev_get_drvdata(&op->dev);
+ struct parport_operations *ops = p->ops;
+
+ parport_remove_port(p);
+
+ if (p->irq != PARPORT_IRQ_NONE) {
+ parport_sunbpp_disable_irq(p);
+ free_irq(p->irq, p);
+ }
+
+ of_iounmap(&op->resource[0], (void __iomem *) p->base, p->size);
+ parport_put_port(p);
+ kfree(ops);
+
+ dev_set_drvdata(&op->dev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id bpp_match[] = {
+ {
+ .name = "SUNW,bpp",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, bpp_match);
+
+static struct platform_driver bpp_sbus_driver = {
+ .driver = {
+ .name = "bpp",
+ .owner = THIS_MODULE,
+ .of_match_table = bpp_match,
+ },
+ .probe = bpp_probe,
+ .remove = bpp_remove,
+};
+
+module_platform_driver(bpp_sbus_driver);
+
+MODULE_AUTHOR("Derrick J Brashear");
+MODULE_DESCRIPTION("Parport Driver for Sparc bidirectional Port");
+MODULE_SUPPORTED_DEVICE("Sparc Bidirectional Parallel Port");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/parport/probe.c b/drivers/parport/probe.c
new file mode 100644
index 00000000..d763bc9e
--- /dev/null
+++ b/drivers/parport/probe.c
@@ -0,0 +1,282 @@
+/*
+ * Parallel port device probing code
+ *
+ * Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ * Philip Blundell
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const struct {
+ const char *token;
+ const char *descr;
+} classes[] = {
+ { "", "Legacy device" },
+ { "PRINTER", "Printer" },
+ { "MODEM", "Modem" },
+ { "NET", "Network device" },
+ { "HDC", "Hard disk" },
+ { "PCMCIA", "PCMCIA" },
+ { "MEDIA", "Multimedia device" },
+ { "FDC", "Floppy disk" },
+ { "PORTS", "Ports" },
+ { "SCANNER", "Scanner" },
+ { "DIGICAM", "Digital camera" },
+ { "", "Unknown device" },
+ { "", "Unspecified" },
+ { "SCSIADAPTER", "SCSI adapter" },
+ { NULL, NULL }
+};
+
+static void pretty_print(struct parport *port, int device)
+{
+ struct parport_device_info *info = &port->probe_info[device + 1];
+
+ printk(KERN_INFO "%s", port->name);
+
+ if (device >= 0)
+ printk (" (addr %d)", device);
+
+ printk (": %s", classes[info->class].descr);
+ if (info->class)
+ printk(", %s %s", info->mfr, info->model);
+
+ printk("\n");
+}
+
+static void parse_data(struct parport *port, int device, char *str)
+{
+ char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
+ char *p = txt, *q;
+ int guessed_class = PARPORT_CLASS_UNSPEC;
+ struct parport_device_info *info = &port->probe_info[device + 1];
+
+ if (!txt) {
+ printk(KERN_WARNING "%s probe: memory squeeze\n", port->name);
+ return;
+ }
+ strcpy(txt, str);
+ while (p) {
+ char *sep;
+ q = strchr(p, ';');
+ if (q) *q = 0;
+ sep = strchr(p, ':');
+ if (sep) {
+ char *u;
+ *(sep++) = 0;
+ /* Get rid of trailing blanks */
+ u = sep + strlen (sep) - 1;
+ while (u >= p && *u == ' ')
+ *u-- = '\0';
+ u = p;
+ while (*u) {
+ *u = toupper(*u);
+ u++;
+ }
+ if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
+ kfree(info->mfr);
+ info->mfr = kstrdup(sep, GFP_KERNEL);
+ } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
+ kfree(info->model);
+ info->model = kstrdup(sep, GFP_KERNEL);
+ } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
+ int i;
+
+ kfree(info->class_name);
+ info->class_name = kstrdup(sep, GFP_KERNEL);
+ for (u = sep; *u; u++)
+ *u = toupper(*u);
+ for (i = 0; classes[i].token; i++) {
+ if (!strcmp(classes[i].token, sep)) {
+ info->class = i;
+ goto rock_on;
+ }
+ }
+ printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
+ info->class = PARPORT_CLASS_OTHER;
+ } else if (!strcmp(p, "CMD") ||
+ !strcmp(p, "COMMAND SET")) {
+ kfree(info->cmdset);
+ info->cmdset = kstrdup(sep, GFP_KERNEL);
+ /* if it speaks printer language, it's
+ probably a printer */
+ if (strstr(sep, "PJL") || strstr(sep, "PCL"))
+ guessed_class = PARPORT_CLASS_PRINTER;
+ } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
+ kfree(info->description);
+ info->description = kstrdup(sep, GFP_KERNEL);
+ }
+ }
+ rock_on:
+ if (q)
+ p = q + 1;
+ else
+ p = NULL;
+ }
+
+ /* If the device didn't tell us its class, maybe we have managed to
+ guess one from the things it did say. */
+ if (info->class == PARPORT_CLASS_UNSPEC)
+ info->class = guessed_class;
+
+ pretty_print (port, device);
+
+ kfree(txt);
+}
+
+/* Read up to count-1 bytes of device id. Terminate buffer with
+ * '\0'. Buffer begins with two Device ID length bytes as given by
+ * device. */
+static ssize_t parport_read_device_id (struct parport *port, char *buffer,
+ size_t count)
+{
+ unsigned char length[2];
+ unsigned lelen, belen;
+ size_t idlens[4];
+ unsigned numidlens;
+ unsigned current_idlen;
+ ssize_t retval;
+ size_t len;
+
+ /* First two bytes are MSB,LSB of inclusive length. */
+ retval = parport_read (port, length, 2);
+
+ if (retval < 0)
+ return retval;
+ if (retval != 2)
+ return -EIO;
+
+ if (count < 2)
+ return 0;
+ memcpy(buffer, length, 2);
+ len = 2;
+
+ /* Some devices wrongly send LE length, and some send it two
+ * bytes short. Construct a sorted array of lengths to try. */
+ belen = (length[0] << 8) + length[1];
+ lelen = (length[1] << 8) + length[0];
+ idlens[0] = min(belen, lelen);
+ idlens[1] = idlens[0]+2;
+ if (belen != lelen) {
+ int off = 2;
+ /* Don't try lengths of 0x100 and 0x200 as 1 and 2 */
+ if (idlens[0] <= 2)
+ off = 0;
+ idlens[off] = max(belen, lelen);
+ idlens[off+1] = idlens[off]+2;
+ numidlens = off+2;
+ }
+ else {
+ /* Some devices don't truly implement Device ID, but
+ * just return constant nibble forever. This catches
+ * also those cases. */
+ if (idlens[0] == 0 || idlens[0] > 0xFFF) {
+ printk (KERN_DEBUG "%s: reported broken Device ID"
+ " length of %#zX bytes\n",
+ port->name, idlens[0]);
+ return -EIO;
+ }
+ numidlens = 2;
+ }
+
+ /* Try to respect the given ID length despite all the bugs in
+ * the ID length. Read according to shortest possible ID
+ * first. */
+ for (current_idlen = 0; current_idlen < numidlens; ++current_idlen) {
+ size_t idlen = idlens[current_idlen];
+ if (idlen+1 >= count)
+ break;
+
+ retval = parport_read (port, buffer+len, idlen-len);
+
+ if (retval < 0)
+ return retval;
+ len += retval;
+
+ if (port->physport->ieee1284.phase != IEEE1284_PH_HBUSY_DAVAIL) {
+ if (belen != len) {
+ printk (KERN_DEBUG "%s: Device ID was %zd bytes"
+ " while device told it would be %d"
+ " bytes\n",
+ port->name, len, belen);
+ }
+ goto done;
+ }
+
+ /* This might end reading the Device ID too
+ * soon. Hopefully the needed fields were already in
+ * the first 256 bytes or so that we must have read so
+ * far. */
+ if (buffer[len-1] == ';') {
+ printk (KERN_DEBUG "%s: Device ID reading stopped"
+ " before device told data not available. "
+ "Current idlen %u of %u, len bytes %02X %02X\n",
+ port->name, current_idlen, numidlens,
+ length[0], length[1]);
+ goto done;
+ }
+ }
+ if (current_idlen < numidlens) {
+ /* Buffer not large enough, read to end of buffer. */
+ size_t idlen, len2;
+ if (len+1 < count) {
+ retval = parport_read (port, buffer+len, count-len-1);
+ if (retval < 0)
+ return retval;
+ len += retval;
+ }
+ /* Read the whole ID since some devices would not
+ * otherwise give back the Device ID from beginning
+ * next time when asked. */
+ idlen = idlens[current_idlen];
+ len2 = len;
+ while(len2 < idlen && retval > 0) {
+ char tmp[4];
+ retval = parport_read (port, tmp,
+ min(sizeof tmp, idlen-len2));
+ if (retval < 0)
+ return retval;
+ len2 += retval;
+ }
+ }
+ /* In addition, there are broken devices out there that don't
+ even finish off with a semi-colon. We do not need to care
+ about those at this time. */
+ done:
+ buffer[len] = '\0';
+ return len;
+}
+
+/* Get Std 1284 Device ID. */
+ssize_t parport_device_id (int devnum, char *buffer, size_t count)
+{
+ ssize_t retval = -ENXIO;
+ struct pardevice *dev = parport_open (devnum, "Device ID probe");
+ if (!dev)
+ return -ENXIO;
+
+ parport_claim_or_block (dev);
+
+ /* Negotiate to compatibility mode, and then to device ID
+ * mode. (This so that we start form beginning of device ID if
+ * already in device ID mode.) */
+ parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
+ retval = parport_negotiate (dev->port,
+ IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID);
+
+ if (!retval) {
+ retval = parport_read_device_id (dev->port, buffer, count);
+ parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
+ if (retval > 2)
+ parse_data (dev->port, dev->daisy, buffer+2);
+ }
+
+ parport_release (dev);
+ parport_close (dev);
+ return retval;
+}
diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c
new file mode 100644
index 00000000..92ed045a
--- /dev/null
+++ b/drivers/parport/procfs.c
@@ -0,0 +1,608 @@
+/* Sysctl interface for parport devices.
+ *
+ * Authors: David Campbell
+ * Tim Waugh
+ * Philip Blundell
+ * Andrea Arcangeli
+ * Riccardo Facchetti
+ *
+ * based on work by Grant Guenther
+ * and Philip Blundell
+ *
+ * Cleaned up include files - Russell King
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
+
+#define PARPORT_MIN_TIMESLICE_VALUE 1ul
+#define PARPORT_MAX_TIMESLICE_VALUE ((unsigned long) HZ)
+#define PARPORT_MIN_SPINTIME_VALUE 1
+#define PARPORT_MAX_SPINTIME_VALUE 1000
+
+static int do_active_device(ctl_table *table, int write,
+ void __user *result, size_t *lenp, loff_t *ppos)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[256];
+ struct pardevice *dev;
+ int len = 0;
+
+ if (write) /* can't happen anyway */
+ return -EACCES;
+
+ if (*ppos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ for (dev = port->devices; dev ; dev = dev->next) {
+ if(dev == port->cad) {
+ len += sprintf(buffer, "%s\n", dev->name);
+ }
+ }
+
+ if(!len) {
+ len += sprintf(buffer, "%s\n", "none");
+ }
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ *ppos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+#ifdef CONFIG_PARPORT_1284
+static int do_autoprobe(ctl_table *table, int write,
+ void __user *result, size_t *lenp, loff_t *ppos)
+{
+ struct parport_device_info *info = table->extra2;
+ const char *str;
+ char buffer[256];
+ int len = 0;
+
+ if (write) /* permissions stop this */
+ return -EACCES;
+
+ if (*ppos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if ((str = info->class_name) != NULL)
+ len += sprintf (buffer + len, "CLASS:%s;\n", str);
+
+ if ((str = info->model) != NULL)
+ len += sprintf (buffer + len, "MODEL:%s;\n", str);
+
+ if ((str = info->mfr) != NULL)
+ len += sprintf (buffer + len, "MANUFACTURER:%s;\n", str);
+
+ if ((str = info->description) != NULL)
+ len += sprintf (buffer + len, "DESCRIPTION:%s;\n", str);
+
+ if ((str = info->cmdset) != NULL)
+ len += sprintf (buffer + len, "COMMAND SET:%s;\n", str);
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ *ppos += len;
+
+ return copy_to_user (result, buffer, len) ? -EFAULT : 0;
+}
+#endif /* IEEE1284.3 support. */
+
+static int do_hardware_base_addr (ctl_table *table, int write,
+ void __user *result,
+ size_t *lenp, loff_t *ppos)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[20];
+ int len = 0;
+
+ if (*ppos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) /* permissions prevent this anyway */
+ return -EACCES;
+
+ len += sprintf (buffer, "%lu\t%lu\n", port->base, port->base_hi);
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ *ppos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+static int do_hardware_irq (ctl_table *table, int write,
+ void __user *result,
+ size_t *lenp, loff_t *ppos)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[20];
+ int len = 0;
+
+ if (*ppos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) /* permissions prevent this anyway */
+ return -EACCES;
+
+ len += sprintf (buffer, "%d\n", port->irq);
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ *ppos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+static int do_hardware_dma (ctl_table *table, int write,
+ void __user *result,
+ size_t *lenp, loff_t *ppos)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[20];
+ int len = 0;
+
+ if (*ppos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) /* permissions prevent this anyway */
+ return -EACCES;
+
+ len += sprintf (buffer, "%d\n", port->dma);
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ *ppos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+static int do_hardware_modes (ctl_table *table, int write,
+ void __user *result,
+ size_t *lenp, loff_t *ppos)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[40];
+ int len = 0;
+
+ if (*ppos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) /* permissions prevent this anyway */
+ return -EACCES;
+
+ {
+#define printmode(x) {if(port->modes&PARPORT_MODE_##x){len+=sprintf(buffer+len,"%s%s",f?",":"",#x);f++;}}
+ int f = 0;
+ printmode(PCSPP);
+ printmode(TRISTATE);
+ printmode(COMPAT);
+ printmode(EPP);
+ printmode(ECP);
+ printmode(DMA);
+#undef printmode
+ }
+ buffer[len++] = '\n';
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ *ppos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+#define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD }
+#define PARPORT_PARPORT_DIR(CHILD) { .procname = "parport", \
+ .mode = 0555, .child = CHILD }
+#define PARPORT_DEV_DIR(CHILD) { .procname = "dev", .mode = 0555, .child = CHILD }
+#define PARPORT_DEVICES_ROOT_DIR { .procname = "devices", \
+ .mode = 0555, .child = NULL }
+
+static const unsigned long parport_min_timeslice_value =
+PARPORT_MIN_TIMESLICE_VALUE;
+
+static const unsigned long parport_max_timeslice_value =
+PARPORT_MAX_TIMESLICE_VALUE;
+
+static const int parport_min_spintime_value =
+PARPORT_MIN_SPINTIME_VALUE;
+
+static const int parport_max_spintime_value =
+PARPORT_MAX_SPINTIME_VALUE;
+
+
+struct parport_sysctl_table {
+ struct ctl_table_header *sysctl_header;
+ ctl_table vars[12];
+ ctl_table device_dir[2];
+ ctl_table port_dir[2];
+ ctl_table parport_dir[2];
+ ctl_table dev_dir[2];
+};
+
+static const struct parport_sysctl_table parport_sysctl_template = {
+ .sysctl_header = NULL,
+ {
+ {
+ .procname = "spintime",
+ .data = NULL,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void*) &parport_min_spintime_value,
+ .extra2 = (void*) &parport_max_spintime_value
+ },
+ {
+ .procname = "base-addr",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_hardware_base_addr
+ },
+ {
+ .procname = "irq",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_hardware_irq
+ },
+ {
+ .procname = "dma",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_hardware_dma
+ },
+ {
+ .procname = "modes",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_hardware_modes
+ },
+ PARPORT_DEVICES_ROOT_DIR,
+#ifdef CONFIG_PARPORT_1284
+ {
+ .procname = "autoprobe",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_autoprobe
+ },
+ {
+ .procname = "autoprobe0",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_autoprobe
+ },
+ {
+ .procname = "autoprobe1",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_autoprobe
+ },
+ {
+ .procname = "autoprobe2",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_autoprobe
+ },
+ {
+ .procname = "autoprobe3",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_autoprobe
+ },
+#endif /* IEEE 1284 support */
+ {}
+ },
+ {
+ {
+ .procname = "active",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = do_active_device
+ },
+ {}
+ },
+ {
+ PARPORT_PORT_DIR(NULL),
+ {}
+ },
+ {
+ PARPORT_PARPORT_DIR(NULL),
+ {}
+ },
+ {
+ PARPORT_DEV_DIR(NULL),
+ {}
+ }
+};
+
+struct parport_device_sysctl_table
+{
+ struct ctl_table_header *sysctl_header;
+ ctl_table vars[2];
+ ctl_table device_dir[2];
+ ctl_table devices_root_dir[2];
+ ctl_table port_dir[2];
+ ctl_table parport_dir[2];
+ ctl_table dev_dir[2];
+};
+
+static const struct parport_device_sysctl_table
+parport_device_sysctl_template = {
+ .sysctl_header = NULL,
+ {
+ {
+ .procname = "timeslice",
+ .data = NULL,
+ .maxlen = sizeof(unsigned long),
+ .mode = 0644,
+ .proc_handler = proc_doulongvec_ms_jiffies_minmax,
+ .extra1 = (void*) &parport_min_timeslice_value,
+ .extra2 = (void*) &parport_max_timeslice_value
+ },
+ },
+ {
+ {
+ .procname = NULL,
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0555,
+ .child = NULL
+ },
+ {}
+ },
+ {
+ PARPORT_DEVICES_ROOT_DIR,
+ {}
+ },
+ {
+ PARPORT_PORT_DIR(NULL),
+ {}
+ },
+ {
+ PARPORT_PARPORT_DIR(NULL),
+ {}
+ },
+ {
+ PARPORT_DEV_DIR(NULL),
+ {}
+ }
+};
+
+struct parport_default_sysctl_table
+{
+ struct ctl_table_header *sysctl_header;
+ ctl_table vars[3];
+ ctl_table default_dir[2];
+ ctl_table parport_dir[2];
+ ctl_table dev_dir[2];
+};
+
+static struct parport_default_sysctl_table
+parport_default_sysctl_table = {
+ .sysctl_header = NULL,
+ {
+ {
+ .procname = "timeslice",
+ .data = &parport_default_timeslice,
+ .maxlen = sizeof(parport_default_timeslice),
+ .mode = 0644,
+ .proc_handler = proc_doulongvec_ms_jiffies_minmax,
+ .extra1 = (void*) &parport_min_timeslice_value,
+ .extra2 = (void*) &parport_max_timeslice_value
+ },
+ {
+ .procname = "spintime",
+ .data = &parport_default_spintime,
+ .maxlen = sizeof(parport_default_spintime),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void*) &parport_min_spintime_value,
+ .extra2 = (void*) &parport_max_spintime_value
+ },
+ {}
+ },
+ {
+ {
+ .procname = "default",
+ .mode = 0555,
+ .child = parport_default_sysctl_table.vars
+ },
+ {}
+ },
+ {
+ PARPORT_PARPORT_DIR(parport_default_sysctl_table.default_dir),
+ {}
+ },
+ {
+ PARPORT_DEV_DIR(parport_default_sysctl_table.parport_dir),
+ {}
+ }
+};
+
+
+int parport_proc_register(struct parport *port)
+{
+ struct parport_sysctl_table *t;
+ int i;
+
+ t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
+
+ t->device_dir[0].extra1 = port;
+
+ for (i = 0; i < 5; i++)
+ t->vars[i].extra1 = port;
+
+ t->vars[0].data = &port->spintime;
+ t->vars[5].child = t->device_dir;
+
+ for (i = 0; i < 5; i++)
+ t->vars[6 + i].extra2 = &port->probe_info[i];
+
+ t->port_dir[0].procname = port->name;
+
+ t->port_dir[0].child = t->vars;
+ t->parport_dir[0].child = t->port_dir;
+ t->dev_dir[0].child = t->parport_dir;
+
+ t->sysctl_header = register_sysctl_table(t->dev_dir);
+ if (t->sysctl_header == NULL) {
+ kfree(t);
+ t = NULL;
+ }
+ port->sysctl_table = t;
+ return 0;
+}
+
+int parport_proc_unregister(struct parport *port)
+{
+ if (port->sysctl_table) {
+ struct parport_sysctl_table *t = port->sysctl_table;
+ port->sysctl_table = NULL;
+ unregister_sysctl_table(t->sysctl_header);
+ kfree(t);
+ }
+ return 0;
+}
+
+int parport_device_proc_register(struct pardevice *device)
+{
+ struct parport_device_sysctl_table *t;
+ struct parport * port = device->port;
+
+ t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
+
+ t->dev_dir[0].child = t->parport_dir;
+ t->parport_dir[0].child = t->port_dir;
+ t->port_dir[0].procname = port->name;
+ t->port_dir[0].child = t->devices_root_dir;
+ t->devices_root_dir[0].child = t->device_dir;
+
+ t->device_dir[0].procname = device->name;
+ t->device_dir[0].child = t->vars;
+ t->vars[0].data = &device->timeslice;
+
+ t->sysctl_header = register_sysctl_table(t->dev_dir);
+ if (t->sysctl_header == NULL) {
+ kfree(t);
+ t = NULL;
+ }
+ device->sysctl_table = t;
+ return 0;
+}
+
+int parport_device_proc_unregister(struct pardevice *device)
+{
+ if (device->sysctl_table) {
+ struct parport_device_sysctl_table *t = device->sysctl_table;
+ device->sysctl_table = NULL;
+ unregister_sysctl_table(t->sysctl_header);
+ kfree(t);
+ }
+ return 0;
+}
+
+static int __init parport_default_proc_register(void)
+{
+ parport_default_sysctl_table.sysctl_header =
+ register_sysctl_table(parport_default_sysctl_table.dev_dir);
+ return 0;
+}
+
+static void __exit parport_default_proc_unregister(void)
+{
+ if (parport_default_sysctl_table.sysctl_header) {
+ unregister_sysctl_table(parport_default_sysctl_table.
+ sysctl_header);
+ parport_default_sysctl_table.sysctl_header = NULL;
+ }
+}
+
+#else /* no sysctl or no procfs*/
+
+int parport_proc_register(struct parport *pp)
+{
+ return 0;
+}
+
+int parport_proc_unregister(struct parport *pp)
+{
+ return 0;
+}
+
+int parport_device_proc_register(struct pardevice *device)
+{
+ return 0;
+}
+
+int parport_device_proc_unregister(struct pardevice *device)
+{
+ return 0;
+}
+
+static int __init parport_default_proc_register (void)
+{
+ return 0;
+}
+
+static void __exit parport_default_proc_unregister (void)
+{
+}
+#endif
+
+module_init(parport_default_proc_register)
+module_exit(parport_default_proc_unregister)
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
new file mode 100644
index 00000000..a848e02e
--- /dev/null
+++ b/drivers/parport/share.c
@@ -0,0 +1,1032 @@
+/*
+ * Parallel-port resource manager code.
+ *
+ * Authors: David Campbell
+ * Tim Waugh
+ * Jose Renau
+ * Philip Blundell
+ * Andrea Arcangeli
+ *
+ * based on work by Grant Guenther
+ * and Philip Blundell
+ *
+ * Any part of this program may be used in documents licensed under
+ * the GNU Free Documentation License, Version 1.1 or any later version
+ * published by the Free Software Foundation.
+ */
+
+#undef PARPORT_DEBUG_SHARING /* undef for production */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include