From e637d553199e264327714da437e6c808f2f4b096 Mon Sep 17 00:00:00 2001 From: Robert Jennings <rcj@linux.vnet.ibm.com> Date: Thu, 22 Jan 2009 13:40:09 -0600 Subject: [SCSI] ibmvscsi: Correct DMA mapping leak The ibmvscsi client driver is not unmapping the SCSI command after encountering a DMA mapping error while trying to map an indirect scattergather list for the event pool. This leads to a leak of DMA entitlement that could result in the device failing future DMA operations in a CMO environment. Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com> Acked-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/ibmvscsi/ibmvscsi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 74d07d137dae..c9aa7611e408 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -432,6 +432,7 @@ static int map_sg_data(struct scsi_cmnd *cmd, sdev_printk(KERN_ERR, cmd->device, "Can't allocate memory " "for indirect table\n"); + scsi_dma_unmap(cmd); return 0; } } -- cgit v1.2.3 From c2f9e49f9bbfa2e111ab1e1628b96b560bae7cec Mon Sep 17 00:00:00 2001 From: James Smart <James.Smart@Emulex.Com> Date: Tue, 27 Jan 2009 11:41:36 -0500 Subject: [SCSI] scsi_scan: add missing interim SDEV_DEL state if slave_alloc fails We were running i/o and performing a bunch of hba resets in a loop. This forces a lot of target removes and then rescans. Since the resets are occuring during scan it's causing the scan i/o to timeout, invoking error recovery, etc. We end up getting some nasty crashing in scsi_scan.c due to references to old sdevs that are failing but had some lingering references that kept them around. Fix by setting device state to SDEV_DEL if the LLD's slave_alloc fails. Signed-off-by: James Smart <james.smart@emulex.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/scsi_scan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 66505bb79410..8f4de20c9deb 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -317,6 +317,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, return sdev; out_device_destroy: + scsi_device_set_state(sdev, SDEV_DEL); transport_destroy_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev); out: -- cgit v1.2.3 From 76e3a19d0691bbfcc559ce77ab3004818fab8f22 Mon Sep 17 00:00:00 2001 From: Martin Peschke <mpeschke@linux.vnet.ibm.com> Date: Fri, 30 Jan 2009 15:46:23 +0100 Subject: [SCSI] sg: fix device number in blktrace data Hi, we have run into an issue with blktrace being started for sg devices. Please apply. Thanks, Martin From: Martin Peschke <mpeschke@linux.vnet.ibm.com> The device number denoting a generic SCSI devices (sg) in a blktrace trace is broken; major and minor are always 0. It looks like sdp->device->sdev_gendev.devt is not initialized properly. The fix below uses other data to make up a valid device number, similar to the way an sg device number is generated for sysfs output. Reported-by: Stefan Raspl <raspl@linux.vnet.ibm.com> Signed-off-by: Martin Peschke <mpeschke@linux.vnet.ibm.com> Acked-by: Douglas Gilbert <dgilbert@interlog.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/sg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 8f0bd3f7a59f..516925d8b570 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1078,7 +1078,7 @@ sg_ioctl(struct inode *inode, struct file *filp, case BLKTRACESETUP: return blk_trace_setup(sdp->device->request_queue, sdp->disk->disk_name, - sdp->device->sdev_gendev.devt, + MKDEV(SCSI_GENERIC_MAJOR, sdp->index), (char *)arg); case BLKTRACESTART: return blk_trace_startstop(sdp->device->request_queue, 1); -- cgit v1.2.3 From d4b17a20f30faf0debbc225bfbf98dba4e351c4d Mon Sep 17 00:00:00 2001 From: Brian King <brking@linux.vnet.ibm.com> Date: Wed, 4 Feb 2009 16:13:08 -0600 Subject: [SCSI] ibmvfc: Fix command timeout errors Currently the ibmvfc driver sets the IBMVFC_CLASS_3_ERR flag in the VFC Frame if both the adapter and the device claim support for Class 3. However, this bit actually refers to Class 3 Error Recovery, which is currently not supported by the VIOS. Setting this bit can cause lots of command timeout responses from the VIOS resulting in general instability. Fix this by never setting this bit. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/ibmvscsi/ibmvfc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index a1a511bdec8c..41226e3a741f 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -1573,9 +1573,6 @@ static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd, vfc_cmd->resp_len = sizeof(vfc_cmd->rsp); vfc_cmd->cancel_key = (unsigned long)cmnd->device->hostdata; vfc_cmd->tgt_scsi_id = rport->port_id; - if ((rport->supported_classes & FC_COS_CLASS3) && - (fc_host_supported_classes(vhost->host) & FC_COS_CLASS3)) - vfc_cmd->flags = IBMVFC_CLASS_3_ERR; vfc_cmd->iu.xfer_len = scsi_bufflen(cmnd); int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun); memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len); -- cgit v1.2.3 From 0883e3b3a85b5860b7729f1279a52e95b87dea97 Mon Sep 17 00:00:00 2001 From: Brian King <brking@linux.vnet.ibm.com> Date: Wed, 4 Feb 2009 16:13:12 -0600 Subject: [SCSI] ibmvfc: Fix rport relogin The ibmvfc driver has a bug in its SCN handling. If it receives an ELS event such asn an N-Port SCN event or an unsolicited PLOGI, or any other SCN event which causes ibmvfc_reinit_host to be called, it is possible that we will call fc_remote_port_add for a target that already has an rport added, which can result in duplicate rports getting created for the same targets. Fix this by calling fc_remote_port_rolechg in this scenario instead to report any possible role change that may have occurred. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/ibmvscsi/ibmvfc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 41226e3a741f..ed1e728763a2 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -3263,6 +3263,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id) return -ENOMEM; } + memset(tgt, 0, sizeof(*tgt)); tgt->scsi_id = scsi_id; tgt->new_scsi_id = scsi_id; tgt->vhost = vhost; @@ -3573,9 +3574,18 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events) static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) { struct ibmvfc_host *vhost = tgt->vhost; - struct fc_rport *rport; + struct fc_rport *rport = tgt->rport; unsigned long flags; + if (rport) { + tgt_dbg(tgt, "Setting rport roles\n"); + fc_remote_port_rolechg(rport, tgt->ids.roles); + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + return; + } + tgt_dbg(tgt, "Adding rport\n"); rport = fc_remote_port_add(vhost->host, 0, &tgt->ids); spin_lock_irqsave(vhost->host->host_lock, flags); -- cgit v1.2.3 From 14ae6faca11889d80f795993dbe932d82305b564 Mon Sep 17 00:00:00 2001 From: Brian King <brking@linux.vnet.ibm.com> Date: Wed, 4 Feb 2009 16:13:13 -0600 Subject: [SCSI] ibmvfc: Increase cancel timeout During cancel testing it has been shown that 15 seconds is not nearly long enough for the VIOS to respond to a cancel under loaded situations. Increasing this timeout to 60 seconds allows time for the VIOS to cancel the outstanding commands and prevents us from escalating to a full host reset, which can take much longer. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/ibmvscsi/ibmvfc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index 87dafd0f8d44..b21e071b9862 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -32,7 +32,7 @@ #define IBMVFC_DRIVER_VERSION "1.0.4" #define IBMVFC_DRIVER_DATE "(November 14, 2008)" -#define IBMVFC_DEFAULT_TIMEOUT 15 +#define IBMVFC_DEFAULT_TIMEOUT 60 #define IBMVFC_INIT_TIMEOUT 120 #define IBMVFC_MAX_REQUESTS_DEFAULT 100 -- cgit v1.2.3 From 7f977ddd0eedfd5aac7865794f220f65aae8f361 Mon Sep 17 00:00:00 2001 From: "Shyam_Iyer@Dell.com" <Shyam_Iyer@Dell.com> Date: Thu, 5 Feb 2009 20:12:37 +0530 Subject: [SCSI] qla2xxx: fix Kernel Panic with Qlogic 2472 Card. Kernel Panic is observed with a Qlogic 2472 Card is plugged into the system and the qla2xxx driver is loaded: QLogic Fibre Channel HBA Driver: 8.02.01.02.11.0-k9 vendor=8086 device=3410 qla2xxx 0000:05:00.0: PCI INT A -> GSI 40 (level, low) -> IRQ 40 qla2xxx 0000:05:00.0: Found an ISP2432, irq 40, iobase 0xffffc2001091c000 qla2xxx 0000:05:00.0: Configuring PCI space... qla2xxx 0000:05:00.0: setting latency timer to 64 qla2xxx 0000:05:00.0: Configure NVRAM parameters... BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 IP: [<ffffffff8036319a>] strncpy+0x5/0x1e PGD 7c564067 PUD 78d8c067 PMD 0 Oops: 0000 [1] SMP last sysfs file: /sys/devices/pci0000:00/0000:00:1d.1/usb6/6-2/6-2:1.1/input/input4/event 4/dev CPU 1 Modules linked in: qla2xxx(+) squashfs usb_storage scsi_transport_fc scsi_tgt parport_pc parport arc4 ecb crypto_blkcipher acpi_cpufreq fan loop nfs nfs_acl lockd sunrpc nls_iso8859_1 nls_cp437 ipv6 af_packet st sr_mod ide_disk ide_cd_mod ide_core cdrom usbhid hid ff_memless sg sd_mod crc_t10dif uhci_hcd mptsas mptscsih ehci_hcd mptbase scsi_transport_sas rtc_cmos rtc_core rtc_lib usbcore scsi_mod thermal bnx2 button processor thermal_sys hwmon edd Supported: Yes Pid: 4415, comm: insmod Not tainted 2.6.27.13-1-default #1 RIP: 0010:[<ffffffff8036319a>] [<ffffffff8036319a>] strncpy+0x5/0x1e RSP: 0018:ffff88007b04fbc0 EFLAGS: 00010202 RAX: 00000000000000b7 RBX: ffff88007b9641e0 RCX: ffff88007c1b2ad7 RDX: 000000000000004f RSI: 0000000000000000 RDI: ffff88007c1b2ad7 RBP: ffff88007c1b0620 R08: 0000000000000010 R09: 0000000100000000 R10: 0000000000000046 R11: ffffffff803651c6 R12: ffff88007b074000 R13: ffff88007b964000 R14: ffff88007c1b2ac6 R15: 0000000000000000 FS: 00007f91a6c366f0(0000) GS:ffff88007dbeee40(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000000 CR3: 000000007bd7c000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process insmod (pid: 4415, threadinfo ffff88007b04e000, task ffff880078586180) Stack: ffffffffa02d82c4 0000000000002432 ffff88007d385000 ffff88007c1b0620 ffff88007c1b0620 ffff88007c1b0000 ffff88007d385000 0000000000002432 ffffffffa02dcb1e 0000000000002432 ffffc2001091c000 ffff88007c1b0620 Call Trace: [<ffffffffa02d82c4>] qla24xx_nvram_config+0x385/0x6c2 [qla2xxx] [<ffffffffa02dcb1e>] qla2x00_initialize_adapter+0x169/0x383 [qla2xxx] [<ffffffffa02f2040>] qla2x00_probe_one+0x6bc/0x9c6 [qla2xxx] [<ffffffff8037346f>] pci_device_probe+0xb8/0x105 [<ffffffff803e5a27>] really_probe+0xdd/0x1e5 [<ffffffff803e5c14>] __driver_attach+0x46/0x6d [<ffffffff803e51e1>] bus_for_each_dev+0x44/0x78 [<ffffffff803e4ac7>] bus_add_driver+0xef/0x235 [<ffffffff803e5dd8>] driver_register+0xa2/0x11f [<ffffffff803736fd>] __pci_register_driver+0x5d/0x90 [<ffffffffa0308126>] qla2x00_module_init+0x126/0x159 [qla2xxx] [<ffffffff80209041>] _stext+0x41/0x110 [<ffffffff80260abd>] sys_init_module+0xa0/0x1ba [<ffffffff8020bfbb>] system_call_fastpath+0x16/0x1b [<00007f91a679b76a>] 0x7f91a679b76a Code: ff c1 41 39 c0 75 05 45 85 c0 75 bf 41 29 c0 44 89 c0 c3 31 d2 8a 04 16 88 04 17 48 ff c2 84 c0 75 f3 48 89 f8 c3 48 89 f9 eb 10 <8a> 06 3c 01 88 01 48 83 de ff 48 ff c1 48 ff ca 48 85 d2 75 eb RIP [<ffffffff8036319a>] strncpy+0x5/0x1e RSP <ffff88007b04fbc0> CR2: 0000000000000000 ---[ end trace 829d7d78dfafb785 ]--- The attached patch fixes the issue. Signed-off-by: Shyam Iyer <shyam_iyer@dell.com> Acked-by: Seokmann Ju <Seokmann.ju@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/qla2xxx/qla_devtbl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_devtbl.h b/drivers/scsi/qla2xxx/qla_devtbl.h index d78d35e681ab..d6ea69df7c5c 100644 --- a/drivers/scsi/qla2xxx/qla_devtbl.h +++ b/drivers/scsi/qla2xxx/qla_devtbl.h @@ -72,7 +72,7 @@ static char *qla2x00_model_name[QLA_MODEL_NAMES*2] = { "QLA2462", "Sun PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x141 */ "QLE2460", "Sun PCI-Express to 2Gb FC, Single Channel", /* 0x142 */ "QLE2462", "Sun PCI-Express to 4Gb FC, Single Channel", /* 0x143 */ - "QEM2462" "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */ + "QEM2462", "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */ "QLE2440", "PCI-Express to 4Gb FC, Single Channel", /* 0x145 */ "QLE2464", "PCI-Express to 4Gb FC, Quad Channel", /* 0x146 */ "QLA2440", "PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x147 */ -- cgit v1.2.3 From 308cec14e6710b4d5b70e9778ce117be8371735d Mon Sep 17 00:00:00 2001 From: Mike Christie <michaelc@cs.wisc.edu> Date: Fri, 6 Feb 2009 12:06:20 -0600 Subject: [SCSI] libiscsi: Fix scsi command timeout oops in iscsi_eh_timed_out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yanling Qi from LSI found the root cause of the panic, below is his analysis: Problem description: the open iscsi driver installs eh_timed_out handler to the blank_transport_template of the scsi middle level that causes panic of timed out command of other host Here are the details Iscsi Session creation During iscsi session creation time, the iscsi_tcp_session_create() of iscsi_tpc.c will create a scsi-host for the session. See the statement marked with the label A. The statement B replaces the shost->transportt point with a local struct variable. static struct iscsi_cls_session * iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, uint16_t qdepth, uint32_t initial_cmdsn, uint32_t *hostno) { struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct Scsi_Host *shost; int cmd_i; if (ep) { printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep); return NULL; } A shost = iscsi_host_alloc(&iscsi_sht, 0, qdepth); if (!shost) return NULL; B shost->transportt = iscsi_tcp_scsi_transport; shost->max_lun = iscsi_max_lun; Please note the scsi host is allocated by invoking isccsi_host_alloc() in libiscsi.c Polluting the middle level blank_transport_template in iscsi_host_alloc() of libiscsi.c The iscsi_host_alloc() invokes the middle level function scsi_host_alloc() in hosts.c for allocating a scsi_host. Then the statement marked with C assigns the iscsi_eh_cmd_timed_out handler to the eh_timed_out callback function. struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht, int dd_data_size, uint16_t qdepth) { struct Scsi_Host *shost; struct iscsi_host *ihost; shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); if (!shost) return NULL; C shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; Please note the shost->transport is the middle level blank_transport_template as shown in the code segment below. We see two problems here. 1. iscsi_eh_cmd_timed_out is installed to the blank_transport_template that will cause some body else problem. 2. iscsi_eh_cmd_timed_out will never be invoked when iscsi command gets timeout because the statement B resets the pointer. Middle level blank_transport_template In the middle level function scsi_host_alloc() of hosts.c, the middle level assigns a blank_transport_template for those hosts not implementing its transport layer. All HBAs without supporting a specific scsi_transport will share the middle level blank_transport_template. Please see the statement D struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) { struct Scsi_Host *shost; gfp_t gfp_mask = GFP_KERNEL; int rval; if (sht->unchecked_isa_dma && privsize) gfp_mask |= __GFP_DMA; shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask); if (!shost) return NULL; shost->host_lock = &shost->default_lock; spin_lock_init(shost->host_lock); shost->shost_state = SHOST_CREATED; INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__targets); INIT_LIST_HEAD(&shost->eh_cmd_q); INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); shost->host_no = scsi_host_next_hn++; /* XXX(hch): still racy */ shost->dma_channel = 0xff; /* These three are default values which can be overridden */ shost->max_channel = 0; shost->max_id = 8; shost->max_lun = 8; /* Give each shost a default transportt */ D shost->transportt = &blank_transport_template; Why we see panic at iscsi_eh_cmd_timed_out() The mpp virtual HBA doesn’t have a specific scsi_transport. Therefore, the blank_transport_template will be assigned to the virtual host of the MPP virtual HBA by SCSI middle level. Please note that the statement C has assigned iscsi-transport eh_timedout handler to the blank_transport_template. When a mpp virtual command gets timedout, the iscsi_eh_cmd_timed_out() will be invoked to handle mpp virtual command timeout from the middle level scsi_times_out() function of the scsi_error.c. enum blk_eh_timer_return scsi_times_out(struct request *req) { struct scsi_cmnd *scmd = req->special; enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED; scsi_log_completion(scmd, TIMEOUT_ERROR); if (scmd->device->host->transportt->eh_timed_out) E eh_timed_out = scmd->device->host->transportt->eh_timed_out; else if (scmd->device->host->hostt->eh_timed_out) eh_timed_out = scmd->device->host->hostt->eh_timed_out; else eh_timed_out = NULL; if (eh_timed_out) { rtn = eh_timed_out(scmd); It is very easy to understand why we get panic in the iscsi_eh_cmd_timed_out(). A scsi_cmnd from a no-iscsi device definitely can not resolve out a session and session->lock. The panic can be happed anywhere during the differencing. static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) { struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct iscsi_conn *conn; enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; cls_session = starget_to_session(scsi_target(scmd->device)); session = cls_session->dd_data; debug_scsi("scsi cmd %p timedout\n", scmd); spin_lock(&session->lock); This patch fixes the problem by moving the setting of the iscsi_eh_cmd_timed_out to iscsi_add_host, which is after the LLDs have set their transport template to shost->transportt. Signed-off-by: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/libiscsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 257c24115de9..809d32d95c76 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1998,6 +1998,8 @@ int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev) if (!shost->can_queue) shost->can_queue = ISCSI_DEF_XMIT_CMDS_MAX; + if (!shost->transportt->eh_timed_out) + shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; return scsi_add_host(shost, pdev); } EXPORT_SYMBOL_GPL(iscsi_host_add); @@ -2020,7 +2022,6 @@ struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht, shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); if (!shost) return NULL; - shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) { if (qdepth != 0) -- cgit v1.2.3 From e916141c6889e2a35869d7057ef1cc5e5a2e86eb Mon Sep 17 00:00:00 2001 From: Julia Lawall <julia@diku.dk> Date: Sun, 8 Feb 2009 22:43:19 +0100 Subject: [SCSI] lpfc: introduce missing kfree Error handling code following a kmalloc should free the allocated data. The semantic match that finds the problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // <smpl> @r exists@ local idexpression x; statement S; expression E; identifier f,l; position p1,p2; expression *ptr != NULL; @@ ( if ((x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...)) == NULL) S | x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S ) <... when != x when != if (...) { <+...x...+> } x->f = E ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // </smpl> Signed-off-by: Julia Lawall <julia@diku.dk> Acked-by: James Smart <james.smart@emulex.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/lpfc/lpfc_els.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index a8f30bdaff69..a7302480bc4a 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -5258,6 +5258,7 @@ lpfc_send_els_event(struct lpfc_vport *vport, sizeof(struct lpfc_name)); break; default: + kfree(els_data); return; } memcpy(els_data->wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name)); -- cgit v1.2.3 From 618a752319503a64d1b66615e8ea2a0e7edaf914 Mon Sep 17 00:00:00 2001 From: Anirban Chakraborty <anirban.chakraborty@qlogic.com> Date: Sun, 8 Feb 2009 20:50:11 -0800 Subject: [SCSI] qla2xxx: Remove interrupt request bit check in the response processing path in multiq mode. Correct response-queue-0 processing by instructing the firmware to run with interrupt-handshaking disabled, similarly to what is now done for all non-0 response queues. Since all response-queues now run in the same mode, the driver no longer needs the hot-path 'is-disabled-HCCR' test. Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com> Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/qla2xxx/qla_gbl.h | 6 ++---- drivers/scsi/qla2xxx/qla_init.c | 7 +++---- drivers/scsi/qla2xxx/qla_isr.c | 10 ---------- drivers/scsi/qla2xxx/qla_mbx.c | 14 ++++++-------- drivers/scsi/qla2xxx/qla_mid.c | 10 +++++----- 5 files changed, 16 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index a336b4bc81a7..bce5b0435188 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -376,10 +376,8 @@ extern int qla2x00_dfs_remove(scsi_qla_host_t *); /* Globa function prototypes for multi-q */ extern int qla25xx_request_irq(struct rsp_que *); -extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *, - uint8_t); -extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *, - uint8_t); +extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *); +extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *); extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t, uint16_t, uint8_t, uint8_t); extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t, diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index f6368a1d3021..986501759ad4 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1226,9 +1226,8 @@ qla24xx_config_rings(struct scsi_qla_host *vha) icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_18); - icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22); + icb->firmware_options_2 &= __constant_cpu_to_le32(~BIT_22); icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_23); - ha->rsp_q_map[0]->options = icb->firmware_options_2; WRT_REG_DWORD(®->isp25mq.req_q_in, 0); WRT_REG_DWORD(®->isp25mq.req_q_out, 0); @@ -3493,7 +3492,7 @@ qla25xx_init_queues(struct qla_hw_data *ha) rsp = ha->rsp_q_map[i]; if (rsp) { rsp->options &= ~BIT_0; - ret = qla25xx_init_rsp_que(base_vha, rsp, rsp->options); + ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) DEBUG2_17(printk(KERN_WARNING "%s Rsp que:%d init failed\n", __func__, @@ -3507,7 +3506,7 @@ qla25xx_init_queues(struct qla_hw_data *ha) if (req) { /* Clear outstanding commands array. */ req->options &= ~BIT_0; - ret = qla25xx_init_req_que(base_vha, req, req->options); + ret = qla25xx_init_req_que(base_vha, req); if (ret != QLA_SUCCESS) DEBUG2_17(printk(KERN_WARNING "%s Req que:%d init failed\n", __func__, diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index e28ad81baf1e..b5554ea4693b 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1707,7 +1707,6 @@ qla25xx_msix_rsp_q(int irq, void *dev_id) struct qla_hw_data *ha; struct rsp_que *rsp; struct device_reg_24xx __iomem *reg; - uint16_t msix_disabled_hccr = 0; rsp = (struct rsp_que *) dev_id; if (!rsp) { @@ -1720,17 +1719,8 @@ qla25xx_msix_rsp_q(int irq, void *dev_id) spin_lock_irq(&ha->hardware_lock); - msix_disabled_hccr = rsp->options; - if (!rsp->id) - msix_disabled_hccr &= __constant_cpu_to_le32(BIT_22); - else - msix_disabled_hccr &= __constant_cpu_to_le32(BIT_6); - qla24xx_process_response_queue(rsp); - if (!msix_disabled_hccr) - WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); - spin_unlock_irq(&ha->hardware_lock); return IRQ_HANDLED; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index f94ffbb98e95..723cd37294c3 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -3090,8 +3090,7 @@ verify_done: } int -qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, - uint8_t options) +qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) { int rval; unsigned long flags; @@ -3101,7 +3100,7 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, struct qla_hw_data *ha = vha->hw; mcp->mb[0] = MBC_INITIALIZE_MULTIQ; - mcp->mb[1] = options; + mcp->mb[1] = req->options; mcp->mb[2] = MSW(LSD(req->dma)); mcp->mb[3] = LSW(LSD(req->dma)); mcp->mb[6] = MSW(MSD(req->dma)); @@ -3128,7 +3127,7 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, mcp->tov = 60; spin_lock_irqsave(&ha->hardware_lock, flags); - if (!(options & BIT_0)) { + if (!(req->options & BIT_0)) { WRT_REG_DWORD(®->req_q_in, 0); WRT_REG_DWORD(®->req_q_out, 0); } @@ -3142,8 +3141,7 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, } int -qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, - uint8_t options) +qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) { int rval; unsigned long flags; @@ -3153,7 +3151,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, struct qla_hw_data *ha = vha->hw; mcp->mb[0] = MBC_INITIALIZE_MULTIQ; - mcp->mb[1] = options; + mcp->mb[1] = rsp->options; mcp->mb[2] = MSW(LSD(rsp->dma)); mcp->mb[3] = LSW(LSD(rsp->dma)); mcp->mb[6] = MSW(MSD(rsp->dma)); @@ -3178,7 +3176,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, mcp->tov = 60; spin_lock_irqsave(&ha->hardware_lock, flags); - if (!(options & BIT_0)) { + if (!(rsp->options & BIT_0)) { WRT_REG_DWORD(®->rsp_q_out, 0); WRT_REG_DWORD(®->rsp_q_in, 0); } diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index f53179c46423..d27ceda50791 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -471,7 +471,7 @@ qla25xx_delete_req_que(struct scsi_qla_host *vha, struct req_que *req) if (req) { req->options |= BIT_0; - ret = qla25xx_init_req_que(vha, req, req->options); + ret = qla25xx_init_req_que(vha, req); } if (ret == QLA_SUCCESS) qla25xx_free_req_que(vha, req); @@ -486,7 +486,7 @@ qla25xx_delete_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) if (rsp) { rsp->options |= BIT_0; - ret = qla25xx_init_rsp_que(vha, rsp, rsp->options); + ret = qla25xx_init_rsp_que(vha, rsp); } if (ret == QLA_SUCCESS) qla25xx_free_rsp_que(vha, rsp); @@ -502,7 +502,7 @@ int qla25xx_update_req_que(struct scsi_qla_host *vha, uint8_t que, uint8_t qos) req->options |= BIT_3; req->qos = qos; - ret = qla25xx_init_req_que(vha, req, req->options); + ret = qla25xx_init_req_que(vha, req); if (ret != QLA_SUCCESS) DEBUG2_17(printk(KERN_WARNING "%s failed\n", __func__)); /* restore options bit */ @@ -632,7 +632,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, req->max_q_depth = ha->req_q_map[0]->max_q_depth; mutex_unlock(&ha->vport_lock); - ret = qla25xx_init_req_que(base_vha, req, options); + ret = qla25xx_init_req_que(base_vha, req); if (ret != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "%s failed\n", __func__); mutex_lock(&ha->vport_lock); @@ -710,7 +710,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, if (ret) goto que_failed; - ret = qla25xx_init_rsp_que(base_vha, rsp, options); + ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "%s failed\n", __func__); mutex_lock(&ha->vport_lock); -- cgit v1.2.3 From 8a659571eccfde1df9bd057d67be51d1aaa0e2db Mon Sep 17 00:00:00 2001 From: Andrew Vasquez <andrew.vasquez@qlogic.com> Date: Sun, 8 Feb 2009 20:50:12 -0800 Subject: [SCSI] qla2xxx: Properly acknowledge IDC notification messages. To ensure smooth operations amongst the FCoE and NIC side components of the ISP81xx chip, the FCoE driver (qla2xxx) must ensure the 10gb NIC driver (qlge) does not timeout waiting for IDC (Inter-Driver Communication) acknowledgments. The acknowledgment requirements are trivial -- a simple mirroring of incoming mailbox registers during the AEN to a process-context capable mailbox command. Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/qla2xxx/qla_def.h | 5 +++++ drivers/scsi/qla2xxx/qla_fw.h | 2 ++ drivers/scsi/qla2xxx/qla_gbl.h | 3 +++ drivers/scsi/qla2xxx/qla_isr.c | 48 ++++++++++++++++++++++++++++++------------ drivers/scsi/qla2xxx/qla_mbx.c | 26 +++++++++++++++++++++++ drivers/scsi/qla2xxx/qla_os.c | 16 ++++++++++++++ 6 files changed, 87 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 023ee77fb027..e0c5bb54b258 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2135,6 +2135,7 @@ struct qla_msix_entry { /* Work events. */ enum qla_work_type { QLA_EVT_AEN, + QLA_EVT_IDC_ACK, }; @@ -2149,6 +2150,10 @@ struct qla_work_evt { enum fc_host_event_code code; u32 data; } aen; + struct { +#define QLA_IDC_ACK_REGS 7 + uint16_t mb[QLA_IDC_ACK_REGS]; + } idc_ack; } u; }; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 7abb045a0410..ffff42554087 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1402,6 +1402,8 @@ struct access_chip_rsp_84xx { #define MBA_IDC_NOTIFY 0x8101 #define MBA_IDC_TIME_EXT 0x8102 +#define MBC_IDC_ACK 0x101 + struct nvram_81xx { /* NVRAM header. */ uint8_t id[4]; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index bce5b0435188..6de283f8f111 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -72,6 +72,7 @@ extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum fc_host_event_code, u32); +extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *); extern void qla2x00_abort_fcport_cmds(fc_port_t *); extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *, @@ -266,6 +267,8 @@ qla2x00_set_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t *); extern int qla84xx_verify_chip(struct scsi_qla_host *, uint16_t *); +extern int qla81xx_idc_ack(scsi_qla_host_t *, uint16_t *); + /* * Global Function Prototypes in qla_isr.c source file. */ diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index b5554ea4693b..f250e5b7897c 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -266,6 +266,40 @@ qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) } } +static void +qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr) +{ + static char *event[] = + { "Complete", "Request Notification", "Time Extension" }; + int rval; + struct device_reg_24xx __iomem *reg24 = &vha->hw->iobase->isp24; + uint16_t __iomem *wptr; + uint16_t cnt, timeout, mb[QLA_IDC_ACK_REGS]; + + /* Seed data -- mailbox1 -> mailbox7. */ + wptr = (uint16_t __iomem *)®24->mailbox1; + for (cnt = 0; cnt < QLA_IDC_ACK_REGS; cnt++, wptr++) + mb[cnt] = RD_REG_WORD(wptr); + + DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- " + "%04x %04x %04x %04x %04x %04x %04x.\n", vha->host_no, + event[aen & 0xff], + mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6])); + + /* Acknowledgement needed? [Notify && non-zero timeout]. */ + timeout = (descr >> 8) & 0xf; + if (aen != MBA_IDC_NOTIFY || !timeout) + return; + + DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- " + "ACK timeout=%d.\n", vha->host_no, event[aen & 0xff], timeout)); + + rval = qla2x00_post_idc_ack_work(vha, mb); + if (rval != QLA_SUCCESS) + qla_printk(KERN_WARNING, vha->hw, + "IDC failed to post ACK.\n"); +} + /** * qla2x00_async_event() - Process aynchronous events. * @ha: SCSI driver HA context @@ -714,21 +748,9 @@ skip_rio: "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3])); break; case MBA_IDC_COMPLETE: - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " - "Complete -- %04x %04x %04x\n", vha->host_no, mb[1], mb[2], - mb[3])); - break; case MBA_IDC_NOTIFY: - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " - "Request Notification -- %04x %04x %04x\n", vha->host_no, - mb[1], mb[2], mb[3])); - /**** Mailbox registers 4 - 7 valid!!! */ - break; case MBA_IDC_TIME_EXT: - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " - "Time Extension -- %04x %04x %04x\n", vha->host_no, mb[1], - mb[2], mb[3])); - /**** Mailbox registers 4 - 7 valid!!! */ + qla81xx_idc_event(vha, mb[0], mb[1]); break; } diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 723cd37294c3..4c7504cb3990 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -3191,3 +3191,29 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) return rval; } +int +qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + + mcp->mb[0] = MBC_IDC_ACK; + memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); + mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, + vha->host_no, rval, mcp->mb[0])); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + } + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index c11f872d3e10..2f5f72531e23 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2522,6 +2522,19 @@ qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code, return qla2x00_post_work(vha, e, 1); } +int +qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK, 1); + if (!e) + return QLA_FUNCTION_FAILED; + + memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); + return qla2x00_post_work(vha, e, 1); +} + static void qla2x00_do_work(struct scsi_qla_host *vha) { @@ -2539,6 +2552,9 @@ qla2x00_do_work(struct scsi_qla_host *vha) fc_host_post_event(vha->host, fc_get_event_number(), e->u.aen.code, e->u.aen.data); break; + case QLA_EVT_IDC_ACK: + qla81xx_idc_ack(vha, e->u.idc_ack.mb); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); -- cgit v1.2.3 From cf5a163127118325296c90670093b14afebb8424 Mon Sep 17 00:00:00 2001 From: Anirban Chakraborty <anirban.chakraborty@qlogic.com> Date: Sun, 8 Feb 2009 20:50:13 -0800 Subject: [SCSI] qla2xxx: Correct slab-error overwrite during vport creation and deletion. The clearing of a vha's req_ques were overrunning during vport creation. During deletion, vport queues should be torn-down after all cleanup has occurred. Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com> Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/qla2xxx/qla_attr.c | 13 ++++++------- drivers/scsi/qla2xxx/qla_mid.c | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 33a3c13fd893..f4c57227ec18 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1265,13 +1265,6 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) msleep(1000); - if (ha->mqenable) { - if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, - "Queue delete failed.\n"); - vha->req_ques[0] = ha->req_q_map[0]->id; - } - qla24xx_disable_vp(vha); fc_remove_host(vha->host); @@ -1293,6 +1286,12 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) vha->host_no, vha->vp_idx, vha)); } + if (ha->mqenable) { + if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS) + qla_printk(KERN_WARNING, ha, + "Queue delete failed.\n"); + } + scsi_host_put(vha->host); qla_printk(KERN_INFO, ha, "vport %d deleted\n", id); return 0; diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index d27ceda50791..3f23932210c4 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -396,7 +396,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL); - memset(vha->req_ques, 0, sizeof(vha->req_ques) * QLA_MAX_HOST_QUES); + memset(vha->req_ques, 0, sizeof(vha->req_ques)); vha->req_ques[0] = ha->req_q_map[0]->id; host->can_queue = ha->req_q_map[0]->length + 128; host->this_id = 255; -- cgit v1.2.3 From 9088608e00d0d9e2a772532d828312e11b118340 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez <andrew.vasquez@qlogic.com> Date: Sun, 8 Feb 2009 20:50:14 -0800 Subject: [SCSI] qla2xxx: Mask out 'reserved' bits while processing FLT regions. Bits 31-8 are marked as reserved and should be ignored while interpreting a region's code. Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/qla2xxx/qla_sup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 9c3b694c049d..284827926eff 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -684,7 +684,7 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) "end=0x%x size=0x%x.\n", le32_to_cpu(region->code), start, le32_to_cpu(region->end) >> 2, le32_to_cpu(region->size))); - switch (le32_to_cpu(region->code)) { + switch (le32_to_cpu(region->code) & 0xff) { case FLT_REG_FW: ha->flt_region_fw = start; break; -- cgit v1.2.3 From 822c05b6335534f74f90bd0edc12aeb5a591117a Mon Sep 17 00:00:00 2001 From: Andrew Vasquez <andrew.vasquez@qlogic.com> Date: Sun, 8 Feb 2009 20:50:16 -0800 Subject: [SCSI] qla2xxx: Update version number to 8.03.00-k3. Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/scsi/qla2xxx/qla_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index cfa4c11a4797..79f7053da99b 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.00-k2" +#define QLA2XXX_VERSION "8.03.00-k3" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 3 -- cgit v1.2.3 From 0b49ec37a20bc7eb7178105aadaa8d1ecba825f8 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox <matthew@wil.cx> Date: Sun, 8 Feb 2009 20:27:47 -0700 Subject: PCI/MSI: fix msi_mask() shift fix Hidetoshi Seto points out that commit bffac3c593eba1f9da3efd0199e49ea6558a40ce has wrong values in the array. Rather than correct the array, we can just use a bounds check and perform the calculation specified in the comment. As a bonus, this will not run off the end of the array if the device specifies an illegal value in the MSI capability. Signed-off-by: Matthew Wilcox <willy@linux.intel.com> Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> --- drivers/pci/msi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 44f15ff70c1d..baba2eb5367d 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -103,14 +103,12 @@ static void msix_set_enable(struct pci_dev *dev, int enable) } } -/* - * Essentially, this is ((1 << (1 << x)) - 1), but without the - * undefinedness of a << 32. - */ static inline __attribute_const__ u32 msi_mask(unsigned x) { - static const u32 mask[] = { 1, 2, 4, 0xf, 0xff, 0xffff, 0xffffffff }; - return mask[x]; + /* Don't shift by >= width of type */ + if (x >= 5) + return 0xffffffff; + return (1 << (1 << x)) - 1; } static void msix_flush_writes(struct irq_desc *desc) -- cgit v1.2.3 From 4cc59c721cba27a4644e29103afac0f91af8da3c Mon Sep 17 00:00:00 2001 From: Randy Dunlap <randy.dunlap@oracle.com> Date: Mon, 9 Feb 2009 09:31:20 -0800 Subject: PCI: fix rom.c kernel-doc warning Fix PCI kernel-doc warning: Warning(linux-2.6.29-rc4-git1/drivers/pci/rom.c:67): No description found for parameter 'pdev' Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> cc: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> --- drivers/pci/rom.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 29cbe47f219f..36864a935d68 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -55,6 +55,7 @@ void pci_disable_rom(struct pci_dev *pdev) /** * pci_get_rom_size - obtain the actual size of the ROM image + * @pdev: target PCI device * @rom: kernel virtual pointer to image of ROM * @size: size of PCI window * return: size of actual ROM image -- cgit v1.2.3 From b33bfdef24565fe54da91adf3cd4eea13488d7fc Mon Sep 17 00:00:00 2001 From: Randy Dunlap <randy.dunlap@oracle.com> Date: Fri, 9 Jan 2009 17:04:26 -0800 Subject: PCI: fix struct pci_platform_pm_ops kernel-doc Fix struct pci_platform_pm_ops kernel-doc notation. Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> --- drivers/pci/pci.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 26ddf78ac300..07c0aa5275e6 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -16,21 +16,21 @@ extern int pci_mmap_fits(struct pci_dev *pdev, int resno, #endif /** - * Firmware PM callbacks + * struct pci_platform_pm_ops - Firmware PM callbacks * - * @is_manageable - returns 'true' if given device is power manageable by the - * platform firmware + * @is_manageable: returns 'true' if given device is power manageable by the + * platform firmware * - * @set_state - invokes the platform firmware to set the device's power state + * @set_state: invokes the platform firmware to set the device's power state * - * @choose_state - returns PCI power state of given device preferred by the - * platform; to be used during system-wide transitions from a - * sleeping state to the working state and vice versa + * @choose_state: returns PCI power state of given device preferred by the + * platform; to be used during system-wide transitions from a + * sleeping state to the working state and vice versa * - * @can_wakeup - returns 'true' if given device is capable of waking up the - * system from a sleeping state + * @can_wakeup: returns 'true' if given device is capable of waking up the + * system from a sleeping state * - * @sleep_wake - enables/disables the system wake up capability of given device + * @sleep_wake: enables/disables the system wake up capability of given device * * If given platform is generally capable of power managing PCI devices, all of * these callbacks are mandatory. -- cgit v1.2.3 From f5ddcac435b6c6133a9c137c345abef53b93cf55 Mon Sep 17 00:00:00 2001 From: Randy Dunlap <randy.dunlap@oracle.com> Date: Fri, 9 Jan 2009 17:03:20 -0800 Subject: PCI: fix missing kernel-doc and typos Fix pci kernel-doc parameter missing notation, correct function name, and fix typo: Warning(linux-2.6.28-git10//drivers/pci/pci.c:1511): No description found for parameter 'exclusive' Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> --- drivers/pci/pci.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e3efe6b19ee7..6d6120007af4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1540,16 +1540,21 @@ void pci_release_region(struct pci_dev *pdev, int bar) } /** - * pci_request_region - Reserved PCI I/O and memory resource + * __pci_request_region - Reserved PCI I/O and memory resource * @pdev: PCI device whose resources are to be reserved * @bar: BAR to be reserved * @res_name: Name to be associated with resource. + * @exclusive: whether the region access is exclusive or not * * Mark the PCI region associated with PCI device @pdev BR @bar as * being reserved by owner @res_name. Do not access any * address inside the PCI regions unless this call returns * successfully. * + * If @exclusive is set, then the region is marked so that userspace + * is explicitly not allowed to map the resource via /dev/mem or + * sysfs MMIO access. + * * Returns 0 on success, or %EBUSY on error. A warning * message is also printed on failure. */ @@ -1588,12 +1593,12 @@ err_out: } /** - * pci_request_region - Reserved PCI I/O and memory resource + * pci_request_region - Reserve PCI I/O and memory resource * @pdev: PCI device whose resources are to be reserved * @bar: BAR to be reserved - * @res_name: Name to be associated with resource. + * @res_name: Name to be associated with resource * - * Mark the PCI region associated with PCI device @pdev BR @bar as + * Mark the PCI region associated with PCI device @pdev BAR @bar as * being reserved by owner @res_name. Do not access any * address inside the PCI regions unless this call returns * successfully. -- cgit v1.2.3 From 12d60e28bed3f593aac5385acbdbb089eb8ae21e Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck <wim@iguana.be> Date: Wed, 28 Jan 2009 20:51:04 +0000 Subject: [WATCHDOG] iTCO_wdt: fix SMI_EN regression 2 bugzilla: #12363 commit 7cd5b08be3c489df11b559fef210b81133764ad4 added a second regression: some Dell's and Compaq's lockup on boot. So we revert most of the code. The ICH9 reboot issue remains in place and will need some more fixing... :-( Signed-off-by: Wim Van Sebroeck <wim@iguana.be> --- drivers/watchdog/Kconfig | 2 +- drivers/watchdog/iTCO_vendor_support.c | 32 +++++++++++++++++++++++++++---- drivers/watchdog/iTCO_wdt.c | 35 ++++++++++++++-------------------- 3 files changed, 43 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 09a3d5522b43..325c10ff6a2c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -406,7 +406,7 @@ config ITCO_WDT ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller - Hub family (from ICH0 up to ICH8) and in the Intel 6300ESB + Hub family (from ICH0 up to ICH10) and in the Intel 63xxESB controller hub. The TCO (Total Cost of Ownership) timer is a watchdog timer diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index 2474ebca88f6..d8264ad0be41 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c @@ -1,7 +1,7 @@ /* * intel TCO vendor specific watchdog driver support * - * (c) Copyright 2006-2008 Wim Van Sebroeck <wim@iguana.be>. + * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,7 +19,7 @@ /* Module and version information */ #define DRV_NAME "iTCO_vendor_support" -#define DRV_VERSION "1.02" +#define DRV_VERSION "1.03" #define PFX DRV_NAME ": " /* Includes */ @@ -77,6 +77,26 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=0 (n * 20.6 seconds. */ +static void supermicro_old_pre_start(unsigned long acpibase) +{ + unsigned long val32; + + /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ + val32 = inl(SMI_EN); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN); /* Needed to activate watchdog */ +} + +static void supermicro_old_pre_stop(unsigned long acpibase) +{ + unsigned long val32; + + /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */ + val32 = inl(SMI_EN); + val32 |= 0x00002000; /* Turn on SMI clearing watchdog */ + outl(val32, SMI_EN); /* Needed to deactivate watchdog */ +} + static void supermicro_old_pre_keepalive(unsigned long acpibase) { /* Reload TCO Timer (done in iTCO_wdt_keepalive) + */ @@ -228,14 +248,18 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) void iTCO_vendor_pre_start(unsigned long acpibase, unsigned int heartbeat) { - if (vendorsupport == SUPERMICRO_NEW_BOARD) + if (vendorsupport == SUPERMICRO_OLD_BOARD) + supermicro_old_pre_start(acpibase); + else if (vendorsupport == SUPERMICRO_NEW_BOARD) supermicro_new_pre_start(heartbeat); } EXPORT_SYMBOL(iTCO_vendor_pre_start); void iTCO_vendor_pre_stop(unsigned long acpibase) { - if (vendorsupport == SUPERMICRO_NEW_BOARD) + if (vendorsupport == SUPERMICRO_OLD_BOARD) + supermicro_old_pre_stop(acpibase); + else if (vendorsupport == SUPERMICRO_NEW_BOARD) supermicro_new_pre_stop(); } EXPORT_SYMBOL(iTCO_vendor_pre_stop); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 5b395a4ddfdf..352334947ea3 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -1,7 +1,7 @@ /* - * intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets) + * intel TCO Watchdog Driver (Used in i82801 and i63xxESB chipsets) * - * (c) Copyright 2006-2008 Wim Van Sebroeck <wim@iguana.be>. + * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,7 +63,7 @@ /* Module and version information */ #define DRV_NAME "iTCO_wdt" -#define DRV_VERSION "1.04" +#define DRV_VERSION "1.05" #define PFX DRV_NAME ": " /* Includes */ @@ -236,16 +236,16 @@ MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); /* Address definitions for the TCO */ /* TCO base address */ -#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 +#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 /* SMI Control and Enable Register */ -#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 +#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 #define TCO_RLD TCOBASE + 0x00 /* TCO Timer Reload and Curr. Value */ #define TCOv1_TMR TCOBASE + 0x01 /* TCOv1 Timer Initial Value */ -#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ -#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ -#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ -#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ +#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ +#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ +#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ +#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ #define TCO1_CNT TCOBASE + 0x08 /* TCO1 Control Register */ #define TCO2_CNT TCOBASE + 0x0a /* TCO2 Control Register */ #define TCOv2_TMR TCOBASE + 0x12 /* TCOv2 Timer Initial Value */ @@ -338,7 +338,6 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) static int iTCO_wdt_start(void) { unsigned int val; - unsigned long val32; spin_lock(&iTCO_wdt_private.io_lock); @@ -351,11 +350,6 @@ static int iTCO_wdt_start(void) return -EIO; } - /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ - val32 = inl(SMI_EN); - val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); - /* Force the timer to its reload value by writing to the TCO_RLD register */ if (iTCO_wdt_private.iTCO_version == 2) @@ -378,7 +372,6 @@ static int iTCO_wdt_start(void) static int iTCO_wdt_stop(void) { unsigned int val; - unsigned long val32; spin_lock(&iTCO_wdt_private.io_lock); @@ -390,11 +383,6 @@ static int iTCO_wdt_stop(void) outw(val, TCO1_CNT); val = inw(TCO1_CNT); - /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */ - val32 = inl(SMI_EN); - val32 |= 0x00002000; - outl(val32, SMI_EN); - /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ iTCO_wdt_set_NO_REBOOT_bit(); @@ -649,6 +637,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, int ret; u32 base_address; unsigned long RCBA; + unsigned long val32; /* * Find the ACPI/PM base I/O address which is the base @@ -695,6 +684,10 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, ret = -EIO; goto out; } + /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ + val32 = inl(SMI_EN); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN); /* The TCO I/O registers reside in a 32-byte range pointed to by the TCOBASE value */ -- cgit v1.2.3 From 2af29b78618ac8b3a8746337002f108f8fdf56ad Mon Sep 17 00:00:00 2001 From: Andrew Victor <linux@maxim.org.za> Date: Wed, 11 Feb 2009 21:23:10 +0100 Subject: [ARM] 5390/1: AT91: Watchdog fixes The recently merged AT91SAM9 watchdog driver uses the AT91SAM9X_WATCHDOG config variable, whereas the original version of the driver (and the platform support code) used AT91SAM9_WATCHDOG. This causes the watchdog platform_device to never be registered, and therefore the driver not to be initialized. This patch: - updates the platform support code to use AT91SAM9X_WATCHDOG. - includes <linux/io.h> to fix compile error (same fix as was applied to at91rm9200_wdt.c) - fixes comment regarding watchdog clock-rates in at91rm9200. Signed-off-by: Andrew Victor <linux@maxim.org.za> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> --- arch/arm/configs/at91sam9260ek_defconfig | 2 +- arch/arm/configs/at91sam9261ek_defconfig | 2 +- arch/arm/configs/at91sam9263ek_defconfig | 2 +- arch/arm/configs/at91sam9rlek_defconfig | 2 +- arch/arm/configs/qil-a9260_defconfig | 2 +- arch/arm/mach-at91/at91cap9_devices.c | 2 +- arch/arm/mach-at91/at91sam9260_devices.c | 2 +- arch/arm/mach-at91/at91sam9261_devices.c | 2 +- arch/arm/mach-at91/at91sam9263_devices.c | 2 +- arch/arm/mach-at91/at91sam9rl_devices.c | 2 +- drivers/watchdog/at91rm9200_wdt.c | 4 ++-- drivers/watchdog/at91sam9_wdt.c | 1 + 12 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/arch/arm/configs/at91sam9260ek_defconfig b/arch/arm/configs/at91sam9260ek_defconfig index e0ee7060f9aa..98e2f3de4bc5 100644 --- a/arch/arm/configs/at91sam9260ek_defconfig +++ b/arch/arm/configs/at91sam9260ek_defconfig @@ -608,7 +608,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set -CONFIG_AT91SAM9_WATCHDOG=y +CONFIG_AT91SAM9X_WATCHDOG=y # # USB-based Watchdog Cards diff --git a/arch/arm/configs/at91sam9261ek_defconfig b/arch/arm/configs/at91sam9261ek_defconfig index 01d1ef97d8be..149456142392 100644 --- a/arch/arm/configs/at91sam9261ek_defconfig +++ b/arch/arm/configs/at91sam9261ek_defconfig @@ -700,7 +700,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set -CONFIG_AT91SAM9_WATCHDOG=y +CONFIG_AT91SAM9X_WATCHDOG=y # # USB-based Watchdog Cards diff --git a/arch/arm/configs/at91sam9263ek_defconfig b/arch/arm/configs/at91sam9263ek_defconfig index 036a126725c1..21599f3c6275 100644 --- a/arch/arm/configs/at91sam9263ek_defconfig +++ b/arch/arm/configs/at91sam9263ek_defconfig @@ -710,7 +710,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set -CONFIG_AT91SAM9_WATCHDOG=y +CONFIG_AT91SAM9X_WATCHDOG=y # # USB-based Watchdog Cards diff --git a/arch/arm/configs/at91sam9rlek_defconfig b/arch/arm/configs/at91sam9rlek_defconfig index 237a2a6a8517..e2df81a3e804 100644 --- a/arch/arm/configs/at91sam9rlek_defconfig +++ b/arch/arm/configs/at91sam9rlek_defconfig @@ -606,7 +606,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set -CONFIG_AT91SAM9_WATCHDOG=y +CONFIG_AT91SAM9X_WATCHDOG=y # # Sonics Silicon Backplane diff --git a/arch/arm/configs/qil-a9260_defconfig b/arch/arm/configs/qil-a9260_defconfig index cd1d717903ac..9b32d0eb89ba 100644 --- a/arch/arm/configs/qil-a9260_defconfig +++ b/arch/arm/configs/qil-a9260_defconfig @@ -727,7 +727,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set -# CONFIG_AT91SAM9_WATCHDOG is not set +# CONFIG_AT91SAM9X_WATCHDOG is not set # # USB-based Watchdog Cards diff --git a/arch/arm/mach-at91/at91cap9_devices.c b/arch/arm/mach-at91/at91cap9_devices.c index 9eca2209cde6..412aa49ad2fb 100644 --- a/arch/arm/mach-at91/at91cap9_devices.c +++ b/arch/arm/mach-at91/at91cap9_devices.c @@ -697,7 +697,7 @@ static void __init at91_add_device_rtt(void) * Watchdog * -------------------------------------------------------------------- */ -#if defined(CONFIG_AT91SAM9_WATCHDOG) || defined(CONFIG_AT91SAM9_WATCHDOG_MODULE) +#if defined(CONFIG_AT91SAM9X_WATCHDOG) || defined(CONFIG_AT91SAM9X_WATCHDOG_MODULE) static struct platform_device at91cap9_wdt_device = { .name = "at91_wdt", .id = -1, diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index fdde1ea21b07..d74c9ac007e7 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -643,7 +643,7 @@ static void __init at91_add_device_rtt(void) * Watchdog * -------------------------------------------------------------------- */ -#if defined(CONFIG_AT91SAM9_WATCHDOG) || defined(CONFIG_AT91SAM9_WATCHDOG_MODULE) +#if defined(CONFIG_AT91SAM9X_WATCHDOG) || defined(CONFIG_AT91SAM9X_WATCHDOG_MODULE) static struct platform_device at91sam9260_wdt_device = { .name = "at91_wdt", .id = -1, diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index 17289756f80f..59fc48311fb0 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c @@ -621,7 +621,7 @@ static void __init at91_add_device_rtt(void) * Watchdog * -------------------------------------------------------------------- */ -#if defined(CONFIG_AT91SAM9_WATCHDOG) || defined(CONFIG_AT91SAM9_WATCHDOG_MODULE) +#if defined(CONFIG_AT91SAM9X_WATCHDOG) || defined(CONFIG_AT91SAM9X_WATCHDOG_MODULE) static struct platform_device at91sam9261_wdt_device = { .name = "at91_wdt", .id = -1, diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index b753cb879d8e..134af97ff340 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -854,7 +854,7 @@ static void __init at91_add_device_rtt(void) * Watchdog * -------------------------------------------------------------------- */ -#if defined(CONFIG_AT91SAM9_WATCHDOG) || defined(CONFIG_AT91SAM9_WATCHDOG_MODULE) +#if defined(CONFIG_AT91SAM9X_WATCHDOG) || defined(CONFIG_AT91SAM9X_WATCHDOG_MODULE) static struct platform_device at91sam9263_wdt_device = { .name = "at91_wdt", .id = -1, diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c index 145324f4ec56..728186515cdf 100644 --- a/arch/arm/mach-at91/at91sam9rl_devices.c +++ b/arch/arm/mach-at91/at91sam9rl_devices.c @@ -609,7 +609,7 @@ static void __init at91_add_device_rtt(void) * Watchdog * -------------------------------------------------------------------- */ -#if defined(CONFIG_AT91SAM9_WATCHDOG) || defined(CONFIG_AT91SAM9_WATCHDOG_MODULE) +#if defined(CONFIG_AT91SAM9X_WATCHDOG) || defined(CONFIG_AT91SAM9X_WATCHDOG_MODULE) static struct platform_device at91sam9rl_wdt_device = { .name = "at91_wdt", .id = -1, diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 5531691f46ea..e35d54589232 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -107,10 +107,10 @@ static int at91_wdt_close(struct inode *inode, struct file *file) static int at91_wdt_settimeout(int new_time) { /* - * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz + * All counting occurs at SLOW_CLOCK / 128 = 256 Hz * * Since WDV is a 16-bit counter, the maximum period is - * 65536 / 0.256 = 256 seconds. + * 65536 / 256 = 256 seconds. */ if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) return -EINVAL; diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index b1da287f90ec..a56ac84381b1 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -18,6 +18,7 @@ #include <linux/errno.h> #include <linux/fs.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/miscdevice.h> #include <linux/module.h> -- cgit v1.2.3 From 9af88143b277f52fc6ce0d69137f435c73c39c1a Mon Sep 17 00:00:00 2001 From: David Woodhouse <dwmw2@infradead.org> Date: Fri, 13 Feb 2009 23:18:03 +0000 Subject: iommu: fix Intel IOMMU write-buffer flushing This is the cause of the DMA faults and disk corruption that people have been seeing. Some chipsets neglect to report the RWBF "capability" -- the flag which says that we need to flush the chipset write-buffer when changing the DMA page tables, to ensure that the change is visible to the IOMMU. Override that bit on the affected chipsets, and everything is happy again. Thanks to Chris and Bhavesh and others for helping to debug. Should resolve: https://bugzilla.redhat.com/show_bug.cgi?id=479996 http://bugzilla.kernel.org/show_bug.cgi?id=12578 Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Tested-and-acked-by: Chris Wright <chrisw@sous-sol.org> Reviewed-by: Bhavesh Davda <bhavesh@vmware.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- drivers/pci/intel-iommu.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f4b7c79023ff..f3f686581a90 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -61,6 +61,8 @@ /* global iommu list, set NULL for ignored DMAR units */ static struct intel_iommu **g_iommus; +static int rwbf_quirk; + /* * 0: Present * 1-11: Reserved @@ -785,7 +787,7 @@ static void iommu_flush_write_buffer(struct intel_iommu *iommu) u32 val; unsigned long flag; - if (!cap_rwbf(iommu->cap)) + if (!rwbf_quirk && !cap_rwbf(iommu->cap)) return; val = iommu->gcmd | DMA_GCMD_WBF; @@ -3137,3 +3139,15 @@ static struct iommu_ops intel_iommu_ops = { .unmap = intel_iommu_unmap_range, .iova_to_phys = intel_iommu_iova_to_phys, }; + +static void __devinit quirk_iommu_rwbf(struct pci_dev *dev) +{ + /* + * Mobile 4 Series Chipset neglects to set RWBF capability, + * but needs it: + */ + printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n"); + rwbf_quirk = 1; +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); -- cgit v1.2.3 From 34edaa88324004baf4884fb0388f86059d9c4878 Mon Sep 17 00:00:00 2001 From: Tobias Diedrich <ranma+kernel@tdiedrich.de> Date: Mon, 16 Feb 2009 00:13:20 -0800 Subject: net: forcedeth: Fix wake-on-lan regression Commit f55c21fd9a92a444e55ad1ca4e4732d56661bf2e ("forcedeth: call restore mac addr in nv_shutdown path"), which was introduced to fix the regression tracked at http://bugzilla.kernel.org/show_bug.cgi?id=11358 causes the wake-on-lan mac to be reversed in the shutdown path. Apparently the forcedeth situation is rather messy in that the mac we need to writeback for a subsequent modprobe to work is exactly the reverse of what is needed for proper wake-on-lan. The following patch explains the situation in the comments and makes the call to nv_restore_mac_addr() conditional (only called if we are not really going for poweroff). Tobias Diedrich wrote: > Hmm, I had not tried WOL for some time. > With 2.6.29-rc3 is see the following behaviour: > > State WOL Behaviour > ------------------------------ > shutdown reversed MAC > disk/shutdown reversed MAC > disk/platform OK > > Apparently nv_restore_mac_addr() restores the MAC in the wrong order > for WOL (at least for my PCI_DEVICE_ID_NVIDIA_NVENET_15). platform > works, because the MAC is not touched in the nv_suspend() path. > > A possible fix might be to only call nv_restore_mac_addr() if > system_state != SYSTEM_POWER_OFF. With the following patch: shutdown OK disk/shutdown OK disk/platform OK kexec OK Signed-off-by: Tobias Diedrich <ranma+kernel@tdiedrich.de> Tested-by: Philipp Matthias Hahn <pmhahn@titan.lahn.de> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/forcedeth.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 5b910cf63740..b8251e827059 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -6011,9 +6011,20 @@ static void nv_shutdown(struct pci_dev *pdev) if (netif_running(dev)) nv_close(dev); - nv_restore_mac_addr(pdev); + /* + * Restore the MAC so a kernel started by kexec won't get confused. + * If we really go for poweroff, we must not restore the MAC, + * otherwise the MAC for WOL will be reversed at least on some boards. + */ + if (system_state != SYSTEM_POWER_OFF) { + nv_restore_mac_addr(pdev); + } pci_disable_device(pdev); + /* + * Apparently it is not possible to reinitialise from D3 hot, + * only put the device into D3 if we really go for poweroff. + */ if (system_state == SYSTEM_POWER_OFF) { if (pci_enable_wake(pdev, PCI_D3cold, np->wolenabled)) pci_enable_wake(pdev, PCI_D3hot, np->wolenabled); -- cgit v1.2.3 From a3c1239eb59c0a907f8be5587d42e950f44543f8 Mon Sep 17 00:00:00 2001 From: David Vrabel <david.vrabel@csr.com> Date: Mon, 16 Feb 2009 14:37:12 +0000 Subject: wusb: whci-hcd: always lock whc->lock with interrupts disabled Always lock whc->lock with spin_lock_irq() or spin_lock_irqsave(). Signed-off-by: David Vrabel <david.vrabel@csr.com> --- drivers/usb/host/whci/asl.c | 4 ++-- drivers/usb/host/whci/pzl.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index 2291c5f5af51..958751ccea43 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -227,13 +227,13 @@ void scan_async_work(struct work_struct *work) * Now that the ASL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index 7dc85a0bee7c..df8b85f07092 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -255,13 +255,13 @@ void scan_periodic_work(struct work_struct *work) * Now that the PZL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** -- cgit v1.2.3 From 744f6592727a7ab9e3ca4266bedaa786825a31bb Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT <gclement00@gmail.com> Date: Mon, 16 Feb 2009 21:21:47 +0100 Subject: [ARM] 5400/1: Add support for inverted rdy_busy pin for Atmel nand device controller Add support for inverted rdy_busy pin for Atmel nand device controller It will fix building error on NeoCore926 board. Acked-by: Andrew Victor <linux@maxim.org.za> Acked-by: David Woodhouse <David.Woodhouse@intel.com> Signed-off-by: Gregory CLEMENT <gclement@adeneo.adetelgroup.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> --- arch/arm/mach-at91/include/mach/board.h | 1 + arch/avr32/mach-at32ap/include/mach/board.h | 1 + drivers/mtd/nand/atmel_nand.c | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index fb51f0e0a83f..0b3ae21b4565 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -93,6 +93,7 @@ struct atmel_nand_data { u8 enable_pin; /* chip enable */ u8 det_pin; /* card detect */ u8 rdy_pin; /* ready/busy */ + u8 rdy_pin_active_low; /* rdy_pin value is inverted */ u8 ale; /* address line number connected to ALE */ u8 cle; /* address line number connected to CLE */ u8 bus_width_16; /* buswidth is 16 bit */ diff --git a/arch/avr32/mach-at32ap/include/mach/board.h b/arch/avr32/mach-at32ap/include/mach/board.h index aafaf7a78886..cff8e84f78f2 100644 --- a/arch/avr32/mach-at32ap/include/mach/board.h +++ b/arch/avr32/mach-at32ap/include/mach/board.h @@ -116,6 +116,7 @@ struct atmel_nand_data { int enable_pin; /* chip enable */ int det_pin; /* card detect */ int rdy_pin; /* ready/busy */ + u8 rdy_pin_active_low; /* rdy_pin value is inverted */ u8 ale; /* address line number connected to ALE */ u8 cle; /* address line number connected to CLE */ u8 bus_width_16; /* buswidth is 16 bit */ diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c98c1570a40b..47a33cec3793 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -139,7 +139,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) struct nand_chip *nand_chip = mtd->priv; struct atmel_nand_host *host = nand_chip->priv; - return gpio_get_value(host->board->rdy_pin); + return gpio_get_value(host->board->rdy_pin) ^ + !!host->board->rdy_pin_active_low; } /* -- cgit v1.2.3 From d1b3525b4126d7acad0493b62642b80b71442661 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov <sshtylyov@ru.mvista.com> Date: Sun, 15 Feb 2009 23:24:24 +0400 Subject: libata-sff: fix 32-bit PIO ATAPI regression Commit 871af1210f13966ab911ed2166e4ab2ce775b99d (libata: Add 32bit PIO support) has caused all kinds of errors on the ATAPI devices, so it has been empirically proven that one shouldn't try to read/write an extra data word when a device is not expecting it already. "Don't do it then"; however, still use a chance to do 32-bit read/write one last time when there are exactly 3 trailing bytes. Oh, and stop pointlessly swapping the bytes to and fro on big-endian machines by using io*_rep() accessors which shouldn't byte-swap. This patch should fix the kernel.org bug #12609. Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com> --- drivers/ata/libata-sff.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 0b299b0f8172..714cb046b594 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -773,18 +773,32 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf, else iowrite32_rep(data_addr, buf, words); + /* Transfer trailing bytes, if any */ if (unlikely(slop)) { - __le32 pad; + unsigned char pad[4]; + + /* Point buf to the tail of buffer */ + buf += buflen - slop; + + /* + * Use io*_rep() accessors here as well to avoid pointlessly + * swapping bytes to and fro on the big endian machines... + */ if (rw == READ) { - pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr)); - memcpy(buf + buflen - slop, &pad, slop); + if (slop < 3) + ioread16_rep(data_addr, pad, 1); + else + ioread32_rep(data_addr, pad, 1); + memcpy(buf, pad, slop); } else { - memcpy(&pad, buf + buflen - slop, slop); - iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr); + memcpy(pad, buf, slop); + if (slop < 3) + iowrite16_rep(data_addr, pad, 1); + else + iowrite32_rep(data_addr, pad, 1); } - words++; } - return words << 2; + return (buflen + 1) & ~1; } EXPORT_SYMBOL_GPL(ata_sff_data_xfer32); -- cgit v1.2.3 From 7dac745b8e367c99175b8f0d014d996f0e5ed9e5 Mon Sep 17 00:00:00 2001 From: Tejun Heo <tj@kernel.org> Date: Thu, 12 Feb 2009 10:34:32 +0900 Subject: sata_nv: give up hardreset on nf2 Kernel bz#12176 reports that nf2 hardreset simply doesn't work. Give up. Argh... Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Robert Hancock <hancockr@shaw.ca> Reported-by: Saro <saro_v@hotmail.it> Signed-off-by: Jeff Garzik <jgarzik@redhat.com> --- drivers/ata/sata_nv.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 444af0415ca1..55a8eed3f3a3 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -421,19 +421,21 @@ static struct ata_port_operations nv_generic_ops = { .hardreset = ATA_OP_NULL, }; -/* OSDL bz3352 reports that nf2/3 controllers can't determine device - * signature reliably. Also, the following thread reports detection - * failure on cold boot with the standard debouncing timing. +/* nf2 is ripe with hardreset related problems. + * + * kernel bz#3352 reports nf2/3 controllers can't determine device + * signature reliably. The following thread reports detection failure + * on cold boot with the standard debouncing timing. * * http://thread.gmane.org/gmane.linux.ide/34098 * - * Debounce with hotplug timing and request follow-up SRST. + * And bz#12176 reports that hardreset simply doesn't work on nf2. + * Give up on it and just don't do hardreset. */ static struct ata_port_operations nv_nf2_ops = { - .inherits = &nv_common_ops, + .inherits = &nv_generic_ops, .freeze = nv_nf2_freeze, .thaw = nv_nf2_thaw, - .hardreset = nv_noclassify_hardreset, }; /* For initial probing after boot and hot plugging, hardreset mostly -- cgit v1.2.3 From 720fd66dfad1b0286721dbb2ed4d6076c0aa953b Mon Sep 17 00:00:00 2001 From: Julia Lawall <julia@diku.dk> Date: Wed, 4 Feb 2009 20:44:01 +0100 Subject: mfd: Fix egpio kzalloc return test Since ei is already known to be non-NULL, I assume that what was intended was to test the result of kzalloc. Signed-off-by: Julia Lawall <julia@diku.dk> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/htc-egpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index 1a4d04664d6d..194df7b8256c 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -307,7 +307,7 @@ static int __init egpio_probe(struct platform_device *pdev) ei->nchips = pdata->num_chips; ei->chip = kzalloc(sizeof(struct egpio_chip) * ei->nchips, GFP_KERNEL); - if (!ei) { + if (!ei->chip) { ret = -ENOMEM; goto fail; } -- cgit v1.2.3 From 62571c29a8343839e85e741db6a489f30686697c Mon Sep 17 00:00:00 2001 From: Mark Brown <broonie@opensource.wolfsonmicro.com> Date: Wed, 4 Feb 2009 20:49:52 +0100 Subject: mfd: Initialise WM8350 interrupts earlier Ensure that the interrupt handling is configured before we do platform specific init. This allows the platform specific initialisation to configure things which use interrupts safely. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/wm8350-core.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index f92595c8f165..70f5e7739546 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1404,15 +1404,6 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, return ret; } - if (pdata && pdata->init) { - ret = pdata->init(wm8350); - if (ret != 0) { - dev_err(wm8350->dev, "Platform init() failed: %d\n", - ret); - goto err; - } - } - mutex_init(&wm8350->auxadc_mutex); mutex_init(&wm8350->irq_mutex); INIT_WORK(&wm8350->irq_work, wm8350_irq_worker); @@ -1430,6 +1421,15 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, } wm8350->chip_irq = irq; + if (pdata && pdata->init) { + ret = pdata->init(wm8350); + if (ret != 0) { + dev_err(wm8350->dev, "Platform init() failed: %d\n", + ret); + goto err; + } + } + wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); wm8350_client_dev_register(wm8350, "wm8350-codec", -- cgit v1.2.3 From 85c93ea7dca475a6ee3bf414befe94b2c42f1001 Mon Sep 17 00:00:00 2001 From: Mark Brown <broonie@opensource.wolfsonmicro.com> Date: Wed, 4 Feb 2009 21:09:38 +0100 Subject: mfd: Improve diagnostics for WM8350 ID register probe Check the return value of the device I/O functions when reading the ID registers so we can provide a more useful diagnostic when we're having trouble talking to the device. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/wm8350-core.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 70f5e7739546..e5e82c7cc523 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1297,14 +1297,29 @@ static void wm8350_client_dev_register(struct wm8350 *wm8350, int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { - int ret = -EINVAL; + int ret; u16 id1, id2, mask_rev; u16 cust_id, mode, chip_rev; /* get WM8350 revision and config mode */ - wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); - wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); - wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), &mask_rev); + ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); + goto err; + } + + ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); + goto err; + } + + ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), + &mask_rev); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to read revision: %d\n", ret); + goto err; + } id1 = be16_to_cpu(id1); id2 = be16_to_cpu(id2); -- cgit v1.2.3 From a39a021fd73ce06aad8d1081ac711a36930e6cb8 Mon Sep 17 00:00:00 2001 From: Mark Brown <broonie@opensource.wolfsonmicro.com> Date: Wed, 4 Feb 2009 21:10:58 +0100 Subject: mfd: Mark WM835x USB_SLV_500MA bit as accessible The code is out of sync with the silicon. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/wm8350-regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index 68887b817d17..9a4cc954cb7c 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -3188,7 +3188,7 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = { { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */ { 0x0000, 0x0000, 0x0000 }, /* R2 */ { 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */ - { 0xFCF7, 0xFCF7, 0xF800 }, /* R4 - System Control 2 */ + { 0xFEF7, 0xFEF7, 0xF800 }, /* R4 - System Control 2 */ { 0x80FF, 0x80FF, 0x8000 }, /* R5 - System Hibernate */ { 0xFB0E, 0xFB0E, 0x0000 }, /* R6 - Interface Control */ { 0x0000, 0x0000, 0x0000 }, /* R7 */ -- cgit v1.2.3 From 29c6a2e6f88225ae2673aabd2de0fa2126653231 Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Wed, 4 Feb 2009 21:23:22 +0100 Subject: mfd: wm8350 tries reaches -1 With a postfix decrement tries will reach -1 rather than 0, so the warning will not be issued even upon timeout. Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/wm8350-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index e5e82c7cc523..ea3801833176 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1111,7 +1111,7 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref) do { schedule_timeout_interruptible(1); reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); - } while (tries-- && (reg & WM8350_AUXADC_POLL)); + } while (--tries && (reg & WM8350_AUXADC_POLL)); if (!tries) dev_err(wm8350->dev, "adc chn %d read timeout\n", channel); -- cgit v1.2.3 From a313d758cc7956d7f1e7a727c8fa571b6468fabf Mon Sep 17 00:00:00 2001 From: Mark Brown <broonie@sirena.org.uk> Date: Wed, 4 Feb 2009 21:26:07 +0100 Subject: mfd: Fix TWL4030 build on some ARM variants Many ARM platforms do not provide a mach/cpu.h so rather than guarding the use of that header with CONFIG_ARM guard it with the guards used when testing for the OMAP variants in the body of the code. Signed-off-by: Mark Brown <broonie@sirena.org.uk> Acked-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/twl4030-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index e7ab0035d305..68826f1e36bc 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -38,7 +38,7 @@ #include <linux/i2c.h> #include <linux/i2c/twl4030.h> -#ifdef CONFIG_ARM +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) #include <mach/cpu.h> #endif -- cgit v1.2.3 From 9427c34bc72f05b519e8466f27c38a3327bae157 Mon Sep 17 00:00:00 2001 From: Philipp Zabel <philipp.zabel@gmail.com> Date: Wed, 4 Feb 2009 21:27:48 +0100 Subject: mfd: fix htc-egpio iomem resource handling using resource_size Fixes an off-by-one error in the iomem resource mapping. Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/htc-egpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index 194df7b8256c..aa266e1f69b2 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -286,7 +286,7 @@ static int __init egpio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) goto fail; - ei->base_addr = ioremap_nocache(res->start, res->end - res->start); + ei->base_addr = ioremap_nocache(res->start, resource_size(res)); if (!ei->base_addr) goto fail; pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr); -- cgit v1.2.3 From 2f161f4485535df85451a8cfdf2487c315f665f5 Mon Sep 17 00:00:00 2001 From: Mark Brown <broonie@opensource.wolfsonmicro.com> Date: Fri, 6 Feb 2009 15:28:15 +0100 Subject: mfd: Ensure all WM8350 IRQs are masked at startup The IRQs might have been left enabled in hardware, generating spurious IRQs before the drivers have registered. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/wm8350-core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index ea3801833176..84d5ea1ec171 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1419,6 +1419,13 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, return ret; } + wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF); + mutex_init(&wm8350->auxadc_mutex); mutex_init(&wm8350->irq_mutex); INIT_WORK(&wm8350->irq_work, wm8350_irq_worker); -- cgit v1.2.3 From 8915e5402809ae6228e15c76417351dad752826e Mon Sep 17 00:00:00 2001 From: Jean Delvare <khali@linux-fr.org> Date: Tue, 17 Feb 2009 09:07:02 +0100 Subject: mfd: terminate pcf50633 i2c_device_id list The i2c_device_id list is supposed to be zero-terminated. Signed-off-by: Jean Delvare <khali@linux-fr.org> Cc: Balaji Rao <balajirrao@openmoko.org> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> --- drivers/mfd/pcf50633-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index ea9488e7ad6d..2e36057659e1 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -678,6 +678,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client) static struct i2c_device_id pcf50633_id_table[] = { {"pcf50633", 0x73}, + {/* end of list */} }; static struct i2c_driver pcf50633_driver = { -- cgit v1.2.3 From 158abca5f699a047ff7b67a64ab19e8ec824e37d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan <adobriyan@gmail.com> Date: Tue, 17 Feb 2009 09:10:19 +0100 Subject: mfd: fix sm501 section mismatches drv => driver renaming is needed otherwise modpost will spit false positives re pointing to __devinit function from regular data. Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Cc: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/sm501.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 0e5761f12634..6c3786f8e9a8 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1321,7 +1321,7 @@ static unsigned int sm501_mem_local[] = { * Common init code for an SM501 */ -static int sm501_init_dev(struct sm501_devdata *sm) +static int __devinit sm501_init_dev(struct sm501_devdata *sm) { struct sm501_initdata *idata; struct sm501_platdata *pdata; @@ -1397,7 +1397,7 @@ static int sm501_init_dev(struct sm501_devdata *sm) return 0; } -static int sm501_plat_probe(struct platform_device *dev) +static int __devinit sm501_plat_probe(struct platform_device *dev) { struct sm501_devdata *sm; int ret; @@ -1586,8 +1586,8 @@ static struct sm501_platdata sm501_pci_platdata = { .gpio_base = -1, }; -static int sm501_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) +static int __devinit sm501_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) { struct sm501_devdata *sm; int err; @@ -1693,7 +1693,7 @@ static void sm501_dev_remove(struct sm501_devdata *sm) sm501_gpio_remove(sm); } -static void sm501_pci_remove(struct pci_dev *dev) +static void __devexit sm501_pci_remove(struct pci_dev *dev) { struct sm501_devdata *sm = pci_get_drvdata(dev); @@ -1727,16 +1727,16 @@ static struct pci_device_id sm501_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, sm501_pci_tbl); -static struct pci_driver sm501_pci_drv = { +static struct pci_driver sm501_pci_driver = { .name = "sm501", .id_table = sm501_pci_tbl, .probe = sm501_pci_probe, - .remove = sm501_pci_remove, + .remove = __devexit_p(sm501_pci_remove), }; MODULE_ALIAS("platform:sm501"); -static struct platform_driver sm501_plat_drv = { +static struct platform_driver sm501_plat_driver = { .driver = { .name = "sm501", .owner = THIS_MODULE, @@ -1749,14 +1749,14 @@ static struct platform_driver sm501_plat_drv = { static int __init sm501_base_init(void) { - platform_driver_register(&sm501_plat_drv); - return pci_register_driver(&sm501_pci_drv); + platform_driver_register(&sm501_plat_driver); + return pci_register_driver(&sm501_pci_driver); } static void __exit sm501_base_exit(void) { - platform_driver_unregister(&sm501_plat_drv); - pci_unregister_driver(&sm501_pci_drv); + platform_driver_unregister(&sm501_plat_driver); + pci_unregister_driver(&sm501_pci_driver); } module_init(sm501_base_init); -- cgit v1.2.3 From dcd9651ecd652a186dd9ad0dde76d43320b9c0a2 Mon Sep 17 00:00:00 2001 From: Rakib Mullick <rakib.mullick@gmail.com> Date: Tue, 17 Feb 2009 09:21:52 +0100 Subject: mfd: Fix sm501_register_gpio section mismatch WARNING: drivers/mfd/built-in.o(.text+0x1706): Section mismatch in reference from the function sm501_register_gpio() to the function .devinit.text:sm501_gpio_register_chip() The function sm501_register_gpio() references the function __devinit sm501_gpio_register_chip(). This is often because sm501_register_gpio lacks a __devinit annotation or the annotation of sm501_gpio_register_chip is wrong. Signed-off-by: Rakib Mullick <rakib.mullick@gmail.com> Signed-off-by: Samuel Ortiz <sameo@openedhand.com> --- drivers/mfd/sm501.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 6c3786f8e9a8..4c7b7962f6b8 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1050,7 +1050,7 @@ static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, return gpiochip_add(gchip); } -static int sm501_register_gpio(struct sm501_devdata *sm) +static int __devinit sm501_register_gpio(struct sm501_devdata *sm) { struct sm501_gpio *gpio = &sm->gpio; resource_size_t iobase = sm->io_res->start + SM501_GPIO; -- cgit v1.2.3 From 35cfd1d964f3c2420862f2167a0eb85ff1208999 Mon Sep 17 00:00:00 2001 From: Michael Tokarev <mjt@tls.msk.ru> Date: Sun, 1 Feb 2009 16:11:04 +0100 Subject: HID: blacklist Powercom USB UPS For quite some time users with various UPSes from Powercom were forced to play magic with bind/unbind in /sys in order to be able to see the UPSes. The beasts does not work as HID devices, even if claims to do so. cypress_m8 driver works with the devices instead, creating a normal serial port with which normal UPS controlling software works. The manufacturer confirmed the upcoming models with proper HID support will have different device IDs. In any way, it's wrong to have two completely different modules for one device in kernel. Blacklist the device in HID (add it to hid_ignore_list) to stop this mess, finally. Signed-off-By: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Jiri Kosina <jkosina@suse.cz> --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6cad69ed21c5..0e96b1fcc14b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1605,6 +1605,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e899f510ebeb..88511970508d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -348,6 +348,9 @@ #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 +#define USB_VENDOR_ID_POWERCOM 0x0d9f +#define USB_DEVICE_ID_POWERCOM_UPS 0x0002 + #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 -- cgit v1.2.3 From dfd395aff4cb16d2480b280064bea1b19ededef1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter <error27@gmail.com> Date: Tue, 3 Feb 2009 16:35:17 +0300 Subject: HID: unlock properly on error paths in hidraw_ioctl() We can't return immediately because lock_kernel() is held. Signed-off-by: Dan Carpenter <error27@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz> --- drivers/hid/hidraw.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 732449628971..02b19db5442e 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -267,8 +267,10 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, default: { struct hid_device *hid = dev->hid; - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) - return -EINVAL; + if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { + ret = -EINVAL; + break; + } if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) { int len; @@ -277,8 +279,9 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, len = strlen(hid->name) + 1; if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->name, len) ? + ret = copy_to_user(user_arg, hid->name, len) ? -EFAULT : len; + break; } if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) { @@ -288,12 +291,13 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, len = strlen(hid->phys) + 1; if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->phys, len) ? + ret = copy_to_user(user_arg, hid->phys, len) ? -EFAULT : len; + break; } } - ret = -ENOTTY; + ret = -ENOTTY; } unlock_kernel(); return ret; -- cgit v1.2.3 From daedb3d6a91f9626ab4c921378ac52e44de833d5 Mon Sep 17 00:00:00 2001 From: Anssi Hannula <anssi.hannula@gmail.com> Date: Sat, 14 Feb 2009 11:45:05 +0200 Subject: HID: move tmff and zpff devices from ignore_list to blacklist The devices handled by hid-tmff and hid-zpff were added in the hid_ignore_list[] instead of hid_blacklist[] in hid-core.c, thus disabling them completely. hid_ignore_list[] causes hid layer to skip the device, while hid_blacklist[] indicates there is a specific driver in hid bus. Re-enable the devices by moving them to the correct list. Signed-off-by: Anssi Hannula <anssi.hannula@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz> --- drivers/hid/hid-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 0e96b1fcc14b..1cc967448f4d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1300,7 +1300,13 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, @@ -1613,10 +1619,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, @@ -1627,8 +1629,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { } }; -- cgit v1.2.3 From ef88f2b563275d156beebc9f76ed134f3f90f210 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab <mchehab@redhat.com> Date: Fri, 13 Feb 2009 08:24:34 -0300 Subject: V4L/DVB (10527): tuner: fix TUV1236D analog/digital setup As reported by David Engel <david@istwok.net>, ATSC115 doesn't work fine with mythtv. This software opens both analog and dvb interfaces of saa7134. What happens is that some tuner commands are going to the wrong place, as shown at the logs: Feb 12 20:37:48 opus kernel: tuner-simple 1-0061: using tuner params #0 (ntsc) Feb 12 20:37:48 opus kernel: tuner-simple 1-0061: freq = 67.25 (1076), range = 0, config = 0xce, cb = 0x01 Feb 12 20:37:48 opus kernel: tuner-simple 1-0061: Freq= 67.25 MHz, V_IF=45.75 MHz, Offset=0.00 MHz, div=1808 Feb 12 20:37:48 opus kernel: tuner 1-0061: tv freq set to 67.25 Feb 12 20:37:48 opus kernel: tuner-simple 1-000a: using tuner params #0 (ntsc) Feb 12 20:37:48 opus kernel: tuner-simple 1-000a: freq = 67.25 (1076), range = 0, config = 0xce, cb = 0x01 Feb 12 20:37:48 opus kernel: tuner-simple 1-000a: Freq= 67.25 MHz, V_IF=45.75 MHz, Offset=0.00 MHz, div=1808 Feb 12 20:37:48 opus kernel: tuner-simple 1-000a: tv 0x07 0x10 0xce 0x01 Feb 12 20:37:48 opus kernel: tuner-simple 1-0061: tv 0x07 0x10 0xce 0x01 This happens due to a hack at TUV1236D analog setup, where it replaces tuner address, at 0x61 for 0x0a, in order to save a few memory bytes. The code assumes that nobody else would try to access the tuner during that setup, but the point is that there's no lock to protect such access. So, this opens the possibility of race conditions to happen. Instead of hacking tuner address, this patch uses a temporary var with the proper tuner value to be used during the setup. This should save the issue, although we should consider to write some analog/digital lock at saa7134 driver. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/common/tuners/tuner-simple.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c index de7adaf5fa5b..78412c9c424a 100644 --- a/drivers/media/common/tuners/tuner-simple.c +++ b/drivers/media/common/tuners/tuner-simple.c @@ -318,7 +318,6 @@ static int simple_std_setup(struct dvb_frontend *fe, u8 *config, u8 *cb) { struct tuner_simple_priv *priv = fe->tuner_priv; - u8 tuneraddr; int rc; /* tv norm specific stuff for multi-norm tuners */ @@ -387,6 +386,7 @@ static int simple_std_setup(struct dvb_frontend *fe, case TUNER_PHILIPS_TUV1236D: { + struct tuner_i2c_props i2c = priv->i2c_props; /* 0x40 -> ATSC antenna input 1 */ /* 0x48 -> ATSC antenna input 2 */ /* 0x00 -> NTSC antenna input 1 */ @@ -398,17 +398,15 @@ static int simple_std_setup(struct dvb_frontend *fe, buffer[1] = 0x04; } /* set to the correct mode (analog or digital) */ - tuneraddr = priv->i2c_props.addr; - priv->i2c_props.addr = 0x0a; - rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[0], 2); + i2c.addr = 0x0a; + rc = tuner_i2c_xfer_send(&i2c, &buffer[0], 2); if (2 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 2)\n", rc); - rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[2], 2); + rc = tuner_i2c_xfer_send(&i2c, &buffer[2], 2); if (2 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 2)\n", rc); - priv->i2c_props.addr = tuneraddr; break; } } -- cgit v1.2.3 From d807dec59d3c850b332b5bf95fe33f18def00068 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz <tobias.lorenz@gmx.net> Date: Thu, 12 Feb 2009 14:56:10 -0300 Subject: V4L/DVB (10532): Correction of Stereo detection/setting and signal strength indication Thanks to Bob Ross <pigiron@gmx.com> - correction of stereo detection/setting - correction of signal strength indicator scaling Signed-off-by: Tobias Lorenz <tobias.lorenz@gmx.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/radio/radio-si470x.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 67cbce82cb91..fd81a97a0fdb 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -98,6 +98,9 @@ * - blacklisted KWorld radio in hid-core.c and hid-ids.h * 2008-12-03 Mark Lord <mlord@pobox.com> * - add support for DealExtreme USB Radio + * 2009-01-31 Bob Ross <pigiron@gmx.com> + * - correction of stereo detection/setting + * - correction of signal strength indicator scaling * * ToDo: * - add firmware download/update support @@ -1385,20 +1388,22 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, }; /* stereo indicator == stereo (instead of mono) */ - if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1) - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - else + if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; /* mono/stereo selector */ - if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 1) - tuner->audmode = V4L2_TUNER_MODE_MONO; - else + if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) tuner->audmode = V4L2_TUNER_MODE_STEREO; + else + tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ - tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI) - * 0x0101; + /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ + tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); + /* the ideal factor is 0xffff/75 = 873,8 */ + tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ -- cgit v1.2.3 From 2f94fc465a6504443bb986ba9d36e28e2b422c6e Mon Sep 17 00:00:00 2001 From: Tobias Lorenz <tobias.lorenz@gmx.net> Date: Thu, 12 Feb 2009 14:56:19 -0300 Subject: V4L/DVB (10533): fix LED status output This patch closes one of my todos that was since long on my list. Some people reported clicks and glitches in the audio stream, correlated to the LED color changing cycle. Thanks to Rick Bronson <rick@efn.org>. Signed-off-by: Tobias Lorenz <tobias.lorenz@gmx.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/radio/radio-si470x.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index fd81a97a0fdb..4dfed6aa2dbc 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -101,11 +101,13 @@ * 2009-01-31 Bob Ross <pigiron@gmx.com> * - correction of stereo detection/setting * - correction of signal strength indicator scaling + * 2009-01-31 Rick Bronson <rick@efn.org> + * Tobias Lorenz <tobias.lorenz@gmx.net> + * - add LED status output * * ToDo: * - add firmware download/update support * - RDS support: interrupt mode, instead of polling - * - add LED status output (check if that's not already done in firmware) */ @@ -884,6 +886,30 @@ static int si470x_rds_on(struct si470x_device *radio) +/************************************************************************** + * General Driver Functions - LED_REPORT + **************************************************************************/ + +/* + * si470x_set_led_state - sets the led state + */ +static int si470x_set_led_state(struct si470x_device *radio, + unsigned char led_state) +{ + unsigned char buf[LED_REPORT_SIZE]; + int retval; + + buf[0] = LED_REPORT; + buf[1] = LED_COMMAND; + buf[2] = led_state; + + retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + + return (retval < 0) ? -EINVAL : 0; +} + + + /************************************************************************** * RDS Driver Functions **************************************************************************/ @@ -1637,6 +1663,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* set initial frequency */ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ + /* set led to connect state */ + si470x_set_led_state(radio, BLINK_GREEN_LED); + /* rds buffer allocation */ radio->buf_size = rds_buf * 3; radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); @@ -1720,6 +1749,9 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) cancel_delayed_work_sync(&radio->work); usb_set_intfdata(intf, NULL); if (radio->users == 0) { + /* set led to disconnect state */ + si470x_set_led_state(radio, BLINK_ORANGE_LED); + video_unregister_device(radio->videodev); kfree(radio->buffer); kfree(radio); -- cgit v1.2.3 From 28100165c3f27f681fee8b60e4e44f64a739c454 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab <mchehab@redhat.com> Date: Mon, 16 Feb 2009 15:27:44 -0300 Subject: V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b On Mon, 02 Feb 2009, Hartmut wrote: This change set is wrong. The affected functions cannot be called from an interrupt context, because they may process large buffers. In this case, interrupts are disabled for a long time. Functions, like dvb_dmx_swfilter_packets(), could be called only from a tasklet. This change set does hide some strong design bugs in dm1105.c and au0828-dvb.c. Please revert this change set and do fix the bugs in dm1105.c and au0828-dvb.c (and other files). On Sun, 15 Feb 2009, Oliver Endriss wrote: This changeset _must_ be reverted! It breaks all kernels since 2.6.27 for applications which use DVB and require a low interrupt latency. It is a very bad idea to call the demuxer to process data buffers with interrupts disabled! On Mon, 16 Feb 2009, Trent Piepho wrote: I agree, this is bad. The demuxer is far too much work to be done with IRQs off. IMHO, even doing it under a spin-lock is excessive. It should be a mutex. Drivers should use a work-queue to feed the demuxer. Thank you for testing this changeset and discovering the issues on it. Cc: Trent Piepho <xyzzy@speakeasy.org> Cc: Hartmut <e9hack@googlemail.com> Cc: Oliver Endriss <o.endriss@gmx.de> Cc: Andreas Oberritter <obi@linuxtv.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/dvb/dvb-core/dmxdev.c | 16 +++++++--------- drivers/media/dvb/dvb-core/dvb_demux.c | 16 ++++++---------- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 0c733c66a441..069d847ba887 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -364,16 +364,15 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, enum dmx_success success) { struct dmxdev_filter *dmxdevfilter = filter->priv; - unsigned long flags; int ret; if (dmxdevfilter->buffer.error) { wake_up(&dmxdevfilter->buffer.queue); return 0; } - spin_lock_irqsave(&dmxdevfilter->dev->lock, flags); + spin_lock(&dmxdevfilter->dev->lock); if (dmxdevfilter->state != DMXDEV_STATE_GO) { - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); return 0; } del_timer(&dmxdevfilter->timer); @@ -392,7 +391,7 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, } if (dmxdevfilter->params.sec.flags & DMX_ONESHOT) dmxdevfilter->state = DMXDEV_STATE_DONE; - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); wake_up(&dmxdevfilter->buffer.queue); return 0; } @@ -404,12 +403,11 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, { struct dmxdev_filter *dmxdevfilter = feed->priv; struct dvb_ringbuffer *buffer; - unsigned long flags; int ret; - spin_lock_irqsave(&dmxdevfilter->dev->lock, flags); + spin_lock(&dmxdevfilter->dev->lock); if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) { - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); return 0; } @@ -419,7 +417,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, else buffer = &dmxdevfilter->dev->dvr_buffer; if (buffer->error) { - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); wake_up(&buffer->queue); return 0; } @@ -430,7 +428,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, dvb_ringbuffer_flush(buffer); buffer->error = ret; } - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); wake_up(&buffer->queue); return 0; } diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index a2c1fd5d2f67..e2eca0b1fe7c 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -399,9 +399,7 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, size_t count) { - unsigned long flags; - - spin_lock_irqsave(&demux->lock, flags); + spin_lock(&demux->lock); while (count--) { if (buf[0] == 0x47) @@ -409,17 +407,16 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, buf += 188; } - spin_unlock_irqrestore(&demux->lock, flags); + spin_unlock(&demux->lock); } EXPORT_SYMBOL(dvb_dmx_swfilter_packets); void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) { - unsigned long flags; int p = 0, i, j; - spin_lock_irqsave(&demux->lock, flags); + spin_lock(&demux->lock); if (demux->tsbufp) { i = demux->tsbufp; @@ -452,18 +449,17 @@ void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) } bailout: - spin_unlock_irqrestore(&demux->lock, flags); + spin_unlock(&demux->lock); } EXPORT_SYMBOL(dvb_dmx_swfilter); void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) { - unsigned long flags; int p = 0, i, j; u8 tmppack[188]; - spin_lock_irqsave(&demux->lock, flags); + spin_lock(&demux->lock); if (demux->tsbufp) { i = demux->tsbufp; @@ -504,7 +500,7 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) } bailout: - spin_unlock_irqrestore(&demux->lock, flags); + spin_unlock(&demux->lock); } EXPORT_SYMBOL(dvb_dmx_swfilter_204); -- cgit v1.2.3 From ad28127d7c7c617bca1d426f95b6ffa1fb8f700f Mon Sep 17 00:00:00 2001 From: Adam Baker <linux@baker-net.org.uk> Date: Wed, 4 Feb 2009 15:33:21 -0300 Subject: V4L/DVB (10619): gspca - main: Destroy the URBs at disconnection time. If a device using the gspca framework is unplugged while it is still streaming then the call that is used to free the URBs that have been allocated occurs after the pointer it uses becomes invalid at the end of gspca_disconnect. Make another cleanup call in gspca_disconnect while the pointer is still valid (multiple calls are OK as destroy_urbs checks for pointers already being NULL. Signed-off-by: Adam Baker <linux@baker-net.org.uk> Signed-off-by: Jean-Francois Moine <moinejf@free.fr> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/video/gspca/gspca.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 2ed24527ecd6..65e4901f4db7 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -422,6 +422,7 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) if (urb == NULL) break; + BUG_ON(!gspca_dev->dev); gspca_dev->urb[i] = NULL; if (!gspca_dev->present) usb_kill_urb(urb); @@ -1950,8 +1951,12 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + mutex_lock(&gspca_dev->usb_lock); gspca_dev->present = 0; + mutex_unlock(&gspca_dev->usb_lock); + destroy_urbs(gspca_dev); + gspca_dev->dev = NULL; usb_set_intfdata(intf, NULL); /* release the device */ -- cgit v1.2.3 From ac9575f75c52bcb455120f8c43376b556acba048 Mon Sep 17 00:00:00 2001 From: Hans Verkuil <hverkuil@xs4all.nl> Date: Sat, 14 Feb 2009 19:58:33 -0300 Subject: V4L/DVB (10625): ivtv: fix decoder crash regression The video_ioctl2 conversion of ivtv in kernel 2.6.27 introduced a bug causing decoder commands to crash. The decoder commands should have been handled from the video_ioctl2 default handler, ensuring correct mapping of the argument between user and kernel space. Unfortunately they ended up before the video_ioctl2 call, causing random crashes. Thanks to hannes@linus.priv.at for testing and helping me track down the cause! Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/video/ivtv/ivtv-ioctl.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index f6b3ef6e691b..9be6244573e9 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1748,6 +1748,18 @@ static long ivtv_default(struct file *file, void *fh, int cmd, void *arg) break; } + case IVTV_IOC_DMA_FRAME: + case VIDEO_GET_PTS: + case VIDEO_GET_FRAME_COUNT: + case VIDEO_GET_EVENT: + case VIDEO_PLAY: + case VIDEO_STOP: + case VIDEO_FREEZE: + case VIDEO_CONTINUE: + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: + return ivtv_decoder_ioctls(file, cmd, (void *)arg); + default: return -EINVAL; } @@ -1790,18 +1802,6 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); return 0; - case IVTV_IOC_DMA_FRAME: - case VIDEO_GET_PTS: - case VIDEO_GET_FRAME_COUNT: - case VIDEO_GET_EVENT: - case VIDEO_PLAY: - case VIDEO_STOP: - case VIDEO_FREEZE: - case VIDEO_CONTINUE: - case VIDEO_COMMAND: - case VIDEO_TRY_COMMAND: - return ivtv_decoder_ioctls(filp, cmd, (void *)arg); - default: break; } -- cgit v1.2.3 From 7bf432d64c47f77111fbce5d48d7774df8b48948 Mon Sep 17 00:00:00 2001 From: Hans Verkuil <hverkuil@xs4all.nl> Date: Mon, 16 Feb 2009 04:25:32 -0300 Subject: V4L/DVB (10626): ivtv: fix regression in get sliced vbi format The new v4l2_subdev_call used s_fmt instead of g_fmt. Thanks-to: Andy Walls <awalls@radix.net> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/video/ivtv/ivtv-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 9be6244573e9..c13bd2aa0bea 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo return 0; } - v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); + v4l2_subdev_call(itv->sd_video, video, g_fmt, fmt); vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } -- cgit v1.2.3 From 603eaa1bdd3e0402085e815cc531bb0a32827a9e Mon Sep 17 00:00:00 2001 From: Jean Delvare <khali@linux-fr.org> Date: Tue, 17 Feb 2009 19:59:54 +0100 Subject: hwmon: (f71882fg) Hide misleading error message If the F71882FG chip is at address 0x4e, then the probe at 0x2e will fail with the following message in the logs: f71882fg: Not a Fintek device This is misleading because there is a Fintek device, just at a different address. So I propose to degrade this message to a debug message. Signed-off-by: Jean Delvare <khali@linux-fr.org> Acked-by: Hans de Goede <hdegoede@redhat.com> --- drivers/hwmon/f71882fg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 609cafff86bc..367f6cb1166c 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1872,7 +1872,7 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, devid = superio_inw(sioaddr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) { - printk(KERN_INFO DRVNAME ": Not a Fintek device\n"); + pr_debug(DRVNAME ": Not a Fintek device\n"); goto exit; } -- cgit v1.2.3 From 18632f84fac770125c0982dfadec6b551e82144e Mon Sep 17 00:00:00 2001 From: Hans de Goede <hdegoede@redhat.com> Date: Tue, 17 Feb 2009 19:59:54 +0100 Subject: hwmon: Fix ACPI resource check error handling This patch fixes a number of cases where things were not properly cleaned up when acpi_check_resource_conflict() returned an error, causing oopses such as the one reported here: https://bugzilla.redhat.com/show_bug.cgi?id=483208 Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Jean Delvare <khali@linux-fr.org> --- drivers/hwmon/f71882fg.c | 2 +- drivers/hwmon/vt1211.c | 2 +- drivers/hwmon/w83627ehf.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 367f6cb1166c..5f81ddf71508 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1932,7 +1932,7 @@ static int __init f71882fg_device_add(unsigned short address, res.name = f71882fg_pdev->name; err = acpi_check_resource_conflict(&res); if (err) - return err; + goto exit_device_put; err = platform_device_add_resources(f71882fg_pdev, &res, 1); if (err) { diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index b0ce37852281..73f77a9b8b18 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -1262,7 +1262,7 @@ static int __init vt1211_device_add(unsigned short address) res.name = pdev->name; err = acpi_check_resource_conflict(&res); if (err) - goto EXIT; + goto EXIT_DEV_PUT; err = platform_device_add_resources(pdev, &res, 1); if (err) { diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index cb808d015361..feae743ba991 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1548,7 +1548,7 @@ static int __init sensors_w83627ehf_init(void) err = acpi_check_resource_conflict(&res); if (err) - goto exit; + goto exit_device_put; err = platform_device_add_resources(pdev, &res, 1); if (err) { -- cgit v1.2.3 From ca77fde8e62cecb2c0769052228d15b901367af8 Mon Sep 17 00:00:00 2001 From: David Woodhouse <dwmw2@infradead.org> Date: Fri, 13 Feb 2009 23:18:03 +0000 Subject: Fix Intel IOMMU write-buffer flushing This is the cause of the DMA faults and disk corruption that people have been seeing. Some chipsets neglect to report the RWBF "capability" -- the flag which says that we need to flush the chipset write-buffer when changing the DMA page tables, to ensure that the change is visible to the IOMMU. Override that bit on the affected chipsets, and everything is happy again. Thanks to Chris and Bhavesh and others for helping to debug. Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Tested-by: Chris Wright <chrisw@sous-sol.org> Reviewed-by: Bhavesh Davda <bhavesh@vmware.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/pci/intel-iommu.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f4b7c79023ff..fa9e41626bfc 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -61,6 +61,8 @@ /* global iommu list, set NULL for ignored DMAR units */ static struct intel_iommu **g_iommus; +static int rwbf_quirk = 0; + /* * 0: Present * 1-11: Reserved @@ -785,7 +787,7 @@ static void iommu_flush_write_buffer(struct intel_iommu *iommu) u32 val; unsigned long flag; - if (!cap_rwbf(iommu->cap)) + if (!rwbf_quirk && !cap_rwbf(iommu->cap)) return; val = iommu->gcmd | DMA_GCMD_WBF; @@ -3137,3 +3139,13 @@ static struct iommu_ops intel_iommu_ops = { .unmap = intel_iommu_unmap_range, .iova_to_phys = intel_iommu_iova_to_phys, }; + +static void __devinit quirk_iommu_rwbf(struct pci_dev *dev) +{ + /* Mobile 4 Series Chipset neglects to set RWBF capability, + but needs it */ + printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n"); + rwbf_quirk = 1; +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); -- cgit v1.2.3 From 3494252d5644993f407a45f01c3e8ad5ae38f93c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" <rjw@sisk.pl> Date: Fri, 13 Feb 2009 23:41:12 +0100 Subject: USB/PCI: Fix resume breakage of controllers behind cardbus bridges If a USB PCI controller is behind a cardbus bridge, we are trying to restore its configuration registers too early, before the cardbus bridge is operational. To fix this, call pci_restore_state() from usb_hcd_pci_resume() and remove usb_hcd_pci_resume_early() which is no longer necessary (the configuration spaces of USB controllers that are not behind cardbus bridges will be restored by the PCI PM core with interrupts disabled anyway). This patch fixes the regression from 2.6.28 tracked as http://bugzilla.kernel.org/show_bug.cgi?id=12659 [ Side note: the proper long-term fix is probably to just force the unplug event at suspend time instead of doing a plug/unplug at resume time, but this patch is fine regardless - Linus ] Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Reported-by: Miles Lane <miles.lane@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/usb/core/hcd-pci.c | 15 ++------------- drivers/usb/core/hcd.h | 1 - drivers/usb/host/ehci-pci.c | 1 - drivers/usb/host/ohci-pci.c | 1 - drivers/usb/host/uhci-hcd.c | 1 - 5 files changed, 2 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index c54fc40458b1..a4301dc02d27 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -297,19 +297,6 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) } EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); -/** - * usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled - * @dev: USB Host Controller being resumed - * - * Store this function in the HCD's struct pci_driver as .resume_early. - */ -int usb_hcd_pci_resume_early(struct pci_dev *dev) -{ - pci_restore_state(dev); - return 0; -} -EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early); - /** * usb_hcd_pci_resume - power management resume of a PCI-based HCD * @dev: USB Host Controller being resumed @@ -333,6 +320,8 @@ int usb_hcd_pci_resume(struct pci_dev *dev) } #endif + pci_restore_state(dev); + hcd = pci_get_drvdata(dev); if (hcd->state != HC_STATE_SUSPENDED) { dev_dbg(hcd->self.controller, diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 5b94a56bec23..f750eb1ab595 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -257,7 +257,6 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev); #ifdef CONFIG_PM extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); -extern int usb_hcd_pci_resume_early(struct pci_dev *dev); extern int usb_hcd_pci_resume(struct pci_dev *dev); #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index bb21fb0a4969..abb9a7706ec7 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -432,7 +432,6 @@ static struct pci_driver ehci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif .shutdown = usb_hcd_pci_shutdown, diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 5d625c3fd423..f9961b4c0da3 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -487,7 +487,6 @@ static struct pci_driver ohci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 944f7e0ca4df..cf5e4cf7ea42 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -942,7 +942,6 @@ static struct pci_driver uhci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif /* PM */ }; -- cgit v1.2.3 From 5955c7a2cfb6a35429adea5dc480002b15ca8cfc Mon Sep 17 00:00:00 2001 From: Zlatko Calusic <zlatko.calusic@iskon.hr> Date: Wed, 18 Feb 2009 01:33:34 +0100 Subject: Add support for VT6415 PCIE PATA IDE Host Controller Signed-off-by: Zlatko Calusic <zlatko.calusic@iskon.hr> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/ata/pata_via.c | 4 +++- include/linux/pci_ids.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 79a6c9a0b721..ba556d3e6963 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -110,7 +110,8 @@ static const struct via_isa_bridge { { "vt8237s", PCI_DEVICE_ID_VIA_8237S, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8251", PCI_DEVICE_ID_VIA_8251, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_SATA_PATA }, - { "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES}, + { "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES }, + { "vt6415", PCI_DEVICE_ID_VIA_6415, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES }, { "vt8237a", PCI_DEVICE_ID_VIA_8237A, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, @@ -593,6 +594,7 @@ static int via_reinit_one(struct pci_dev *pdev) #endif static const struct pci_device_id via[] = { + { PCI_VDEVICE(VIA, 0x0415), }, { PCI_VDEVICE(VIA, 0x0571), }, { PCI_VDEVICE(VIA, 0x0581), }, { PCI_VDEVICE(VIA, 0x1571), }, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 52a9fe08451c..918391b4b109 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1312,6 +1312,7 @@ #define PCI_DEVICE_ID_VIA_VT3351 0x0351 #define PCI_DEVICE_ID_VIA_VT3364 0x0364 #define PCI_DEVICE_ID_VIA_8371_0 0x0391 +#define PCI_DEVICE_ID_VIA_6415 0x0415 #define PCI_DEVICE_ID_VIA_8501_0 0x0501 #define PCI_DEVICE_ID_VIA_82C561 0x0561 #define PCI_DEVICE_ID_VIA_82C586_1 0x0571 -- cgit v1.2.3 From 444122fd58fdc83c96877a92b3f6288cafddb08d Mon Sep 17 00:00:00 2001 From: Yi Li <yi.li@analog.com> Date: Thu, 5 Feb 2009 15:31:57 +0800 Subject: MMC: fix bug - SDHC card capacity not correct Signed-off-by: Yi Li <yi.li@analog.com> Signed-off-by: Bryan Wu <cooloney@kernel.org> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/card/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 45b1f430685f..513eb09a638f 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -584,7 +584,7 @@ static int mmc_blk_probe(struct mmc_card *card) if (err) goto out; - string_get_size(get_capacity(md->disk) << 9, STRING_UNITS_2, + string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2, cap_str, sizeof(cap_str)); printk(KERN_INFO "%s: %s %s %s %s\n", md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), -- cgit v1.2.3 From 86a6a8749d5b8fd5c2b544fe9fd11101e3d0550f Mon Sep 17 00:00:00 2001 From: Pierre Ossman <drzeus@drzeus.cx> Date: Mon, 2 Feb 2009 21:13:49 +0100 Subject: Revert "sdhci: force high speed capability on some controllers" This reverts commit a4b76193774b463b922cab2f92450efb20d29ef0. It turned out that the controller had problem running at the higher speed, so go back to trusting the hardware capability bits. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/host/sdhci-pci.c | 3 +-- drivers/mmc/host/sdhci.c | 3 +-- drivers/mmc/host/sdhci.h | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index f07255cb17ee..8cff5f5e7f86 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -144,8 +144,7 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE | SDHCI_QUIRK_RESET_AFTER_REQUEST | - SDHCI_QUIRK_BROKEN_SMALL_PIO | - SDHCI_QUIRK_FORCE_HIGHSPEED; + SDHCI_QUIRK_BROKEN_SMALL_PIO; } /* diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6b2d1f99af67..24d7414a3315 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1636,8 +1636,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->f_max = host->max_clk; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; - if ((caps & SDHCI_CAN_DO_HISPD) || - (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED)) + if (caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED; mmc->ocr_avail = 0; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 3efba2363941..ffeab227d95b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -208,8 +208,6 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12) /* Controller has an issue with buffer bits for small transfers */ #define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) -/* Controller supports high speed but doesn't have the caps bit set */ -#define SDHCI_QUIRK_FORCE_HIGHSPEED (1<<14) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ -- cgit v1.2.3 From 93dbb393503d53cd226e5e1f0088fe8f4dbaa2b8 Mon Sep 17 00:00:00 2001 From: Jens Axboe <jens.axboe@oracle.com> Date: Mon, 16 Feb 2009 10:25:40 +0100 Subject: block: fix bad definition of BIO_RW_SYNC We can't OR shift values, so get rid of BIO_RW_SYNC and use BIO_RW_SYNCIO and BIO_RW_UNPLUG explicitly. This brings back the behaviour from before 213d9417fec62ef4c3675621b9364a667954d4dd. Signed-off-by: Jens Axboe <jens.axboe@oracle.com> --- block/blktrace.c | 2 +- drivers/md/dm-io.c | 2 +- drivers/md/dm-kcopyd.c | 2 +- drivers/md/md.c | 4 ++-- include/linux/bio.h | 2 -- include/linux/blktrace_api.h | 1 + include/linux/fs.h | 6 +++--- kernel/power/swap.c | 5 +++-- mm/page_io.c | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/block/blktrace.c b/block/blktrace.c index 39cc3bfe56e4..7cf9d1ff45a0 100644 --- a/block/blktrace.c +++ b/block/blktrace.c @@ -142,7 +142,7 @@ static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, what |= ddir_act[rw & WRITE]; what |= MASK_TC_BIT(rw, BARRIER); - what |= MASK_TC_BIT(rw, SYNC); + what |= MASK_TC_BIT(rw, SYNCIO); what |= MASK_TC_BIT(rw, AHEAD); what |= MASK_TC_BIT(rw, META); what |= MASK_TC_BIT(rw, DISCARD); diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index a34338567a2a..f14813be4eff 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -328,7 +328,7 @@ static void dispatch_io(int rw, unsigned int num_regions, struct dpages old_pages = *dp; if (sync) - rw |= (1 << BIO_RW_SYNC); + rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); /* * For multiple regions we need to be careful to rewind diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 3073618269ea..0a225da21272 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -344,7 +344,7 @@ static int run_io_job(struct kcopyd_job *job) { int r; struct dm_io_request io_req = { - .bi_rw = job->rw | (1 << BIO_RW_SYNC), + .bi_rw = job->rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG), .mem.type = DM_IO_PAGE_LIST, .mem.ptr.pl = job->pages, .mem.offset = job->offset, diff --git a/drivers/md/md.c b/drivers/md/md.c index 4495104f6c9f..03b4cd0a6344 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -474,7 +474,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, * causes ENOTSUPP, we allocate a spare bio... */ struct bio *bio = bio_alloc(GFP_NOIO, 1); - int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNC); + int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG); bio->bi_bdev = rdev->bdev; bio->bi_sector = sector; @@ -531,7 +531,7 @@ int sync_page_io(struct block_device *bdev, sector_t sector, int size, struct completion event; int ret; - rw |= (1 << BIO_RW_SYNC); + rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); bio->bi_bdev = bdev; bio->bi_sector = sector; diff --git a/include/linux/bio.h b/include/linux/bio.h index 2aa283ab062b..1b16108a5417 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -171,8 +171,6 @@ struct bio { #define BIO_RW_FAILFAST_TRANSPORT 8 #define BIO_RW_FAILFAST_DRIVER 9 -#define BIO_RW_SYNC (BIO_RW_SYNCIO | BIO_RW_UNPLUG) - #define bio_rw_flagged(bio, flag) ((bio)->bi_rw & (1 << (flag))) /* diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 25379cba2370..6e915878e88c 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -15,6 +15,7 @@ enum blktrace_cat { BLK_TC_WRITE = 1 << 1, /* writes */ BLK_TC_BARRIER = 1 << 2, /* barrier */ BLK_TC_SYNC = 1 << 3, /* sync IO */ + BLK_TC_SYNCIO = BLK_TC_SYNC, BLK_TC_QUEUE = 1 << 4, /* queueing/merging */ BLK_TC_REQUEUE = 1 << 5, /* requeueing */ BLK_TC_ISSUE = 1 << 6, /* issue */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 6022f44043f2..67857dc16365 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -87,10 +87,10 @@ struct inodes_stat_t { #define WRITE 1 #define READA 2 /* read-ahead - don't block if no resources */ #define SWRITE 3 /* for ll_rw_block() - wait for buffer lock */ -#define READ_SYNC (READ | (1 << BIO_RW_SYNC)) +#define READ_SYNC (READ | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG)) #define READ_META (READ | (1 << BIO_RW_META)) -#define WRITE_SYNC (WRITE | (1 << BIO_RW_SYNC)) -#define SWRITE_SYNC (SWRITE | (1 << BIO_RW_SYNC)) +#define WRITE_SYNC (WRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG)) +#define SWRITE_SYNC (SWRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG)) #define WRITE_BARRIER (WRITE | (1 << BIO_RW_BARRIER)) #define DISCARD_NOBARRIER (1 << BIO_RW_DISCARD) #define DISCARD_BARRIER ((1 << BIO_RW_DISCARD) | (1 << BIO_RW_BARRIER)) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 6da14358537c..505f319e489c 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -60,6 +60,7 @@ static struct block_device *resume_bdev; static int submit(int rw, pgoff_t page_off, struct page *page, struct bio **bio_chain) { + const int bio_rw = rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); struct bio *bio; bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1); @@ -80,7 +81,7 @@ static int submit(int rw, pgoff_t page_off, struct page *page, bio_get(bio); if (bio_chain == NULL) { - submit_bio(rw | (1 << BIO_RW_SYNC), bio); + submit_bio(bio_rw, bio); wait_on_page_locked(page); if (rw == READ) bio_set_pages_dirty(bio); @@ -90,7 +91,7 @@ static int submit(int rw, pgoff_t page_off, struct page *page, get_page(page); /* These pages are freed later */ bio->bi_private = *bio_chain; *bio_chain = bio; - submit_bio(rw | (1 << BIO_RW_SYNC), bio); + submit_bio(bio_rw, bio); } return 0; } diff --git a/mm/page_io.c b/mm/page_io.c index dc6ce0afbded..3023c475e041 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -111,7 +111,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) goto out; } if (wbc->sync_mode == WB_SYNC_ALL) - rw |= (1 << BIO_RW_SYNC); + rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); count_vm_event(PSWPOUT); set_page_writeback(page); unlock_page(page); -- cgit v1.2.3 From c8cbec6bdf6329279fd14696020f6b59d1d3124d Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Mon, 16 Feb 2009 13:11:55 +0100 Subject: paride/pg.c: xs(): &&/|| confusion &&/|| confusion Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jens Axboe <jens.axboe@oracle.com> --- drivers/block/paride/pg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 9dfa27163001..c397b3ddba9b 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -422,7 +422,7 @@ static void xs(char *buf, char *targ, int len) for (k = 0; k < len; k++) { char c = *buf++; - if (c != ' ' || c != l) + if (c != ' ' && c != l) l = *targ++ = c; } if (l == ' ') -- cgit v1.2.3 From 82eb03cfd862a65363fa2826de0dbd5474cfe5e2 Mon Sep 17 00:00:00 2001 From: Chip Coldwell <coldwell@redhat.com> Date: Mon, 16 Feb 2009 13:11:56 +0100 Subject: cciss: PCI power management reset for kexec The kexec kernel resets the CCISS hardware in three steps: 1. Use PCI power management states to reset the controller in the kexec kernel. 2. Clear the MSI/MSI-X bits in PCI configuration space so that MSI initialization in the kexec kernel doesn't fail. 3. Use the CCISS "No-op" message to determine when the controller firmware has recovered from the PCI PM reset. [akpm@linux-foundation.org: cleanups] Signed-off-by: Mike Miller <mike.miller@hp.com> Cc: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jens Axboe <jens.axboe@oracle.com> --- drivers/block/cciss.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) (limited to 'drivers') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 01e69383d9c0..d2cb67b61176 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -3390,6 +3390,203 @@ static void free_hba(int i) kfree(p); } +/* Send a message CDB to the firmware. */ +static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, unsigned char type) +{ + typedef struct { + CommandListHeader_struct CommandHeader; + RequestBlock_struct Request; + ErrDescriptor_struct ErrorDescriptor; + } Command; + static const size_t cmd_sz = sizeof(Command) + sizeof(ErrorInfo_struct); + Command *cmd; + dma_addr_t paddr64; + uint32_t paddr32, tag; + void __iomem *vaddr; + int i, err; + + vaddr = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if (vaddr == NULL) + return -ENOMEM; + + /* The Inbound Post Queue only accepts 32-bit physical addresses for the + CCISS commands, so they must be allocated from the lower 4GiB of + memory. */ + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + iounmap(vaddr); + return -ENOMEM; + } + + cmd = pci_alloc_consistent(pdev, cmd_sz, &paddr64); + if (cmd == NULL) { + iounmap(vaddr); + return -ENOMEM; + } + + /* This must fit, because of the 32-bit consistent DMA mask. Also, + although there's no guarantee, we assume that the address is at + least 4-byte aligned (most likely, it's page-aligned). */ + paddr32 = paddr64; + + cmd->CommandHeader.ReplyQueue = 0; + cmd->CommandHeader.SGList = 0; + cmd->CommandHeader.SGTotal = 0; + cmd->CommandHeader.Tag.lower = paddr32; + cmd->CommandHeader.Tag.upper = 0; + memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8); + + cmd->Request.CDBLen = 16; + cmd->Request.Type.Type = TYPE_MSG; + cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE; + cmd->Request.Type.Direction = XFER_NONE; + cmd->Request.Timeout = 0; /* Don't time out */ + cmd->Request.CDB[0] = opcode; + cmd->Request.CDB[1] = type; + memset(&cmd->Request.CDB[2], 0, 14); /* the rest of the CDB is reserved */ + + cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(Command); + cmd->ErrorDescriptor.Addr.upper = 0; + cmd->ErrorDescriptor.Len = sizeof(ErrorInfo_struct); + + writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET); + + for (i = 0; i < 10; i++) { + tag = readl(vaddr + SA5_REPLY_PORT_OFFSET); + if ((tag & ~3) == paddr32) + break; + schedule_timeout_uninterruptible(HZ); + } + + iounmap(vaddr); + + /* we leak the DMA buffer here ... no choice since the controller could + still complete the command. */ + if (i == 10) { + printk(KERN_ERR "cciss: controller message %02x:%02x timed out\n", + opcode, type); + return -ETIMEDOUT; + } + + pci_free_consistent(pdev, cmd_sz, cmd, paddr64); + + if (tag & 2) { + printk(KERN_ERR "cciss: controller message %02x:%02x failed\n", + opcode, type); + return -EIO; + } + + printk(KERN_INFO "cciss: controller message %02x:%02x succeeded\n", + opcode, type); + return 0; +} + +#define cciss_soft_reset_controller(p) cciss_message(p, 1, 0) +#define cciss_noop(p) cciss_message(p, 3, 0) + +static __devinit int cciss_reset_msi(struct pci_dev *pdev) +{ +/* the #defines are stolen from drivers/pci/msi.h. */ +#define msi_control_reg(base) (base + PCI_MSI_FLAGS) +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) + + int pos; + u16 control = 0; + + pos = pci_find_capability(pdev, PCI_CAP_ID_MSI); + if (pos) { + pci_read_config_word(pdev, msi_control_reg(pos), &control); + if (control & PCI_MSI_FLAGS_ENABLE) { + printk(KERN_INFO "cciss: resetting MSI\n"); + pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE); + } + } + + pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_word(pdev, msi_control_reg(pos), &control); + if (control & PCI_MSIX_FLAGS_ENABLE) { + printk(KERN_INFO "cciss: resetting MSI-X\n"); + pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE); + } + } + + return 0; +} + +/* This does a hard reset of the controller using PCI power management + * states. */ +static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev) +{ + u16 pmcsr, saved_config_space[32]; + int i, pos; + + printk(KERN_INFO "cciss: using PCI PM to reset controller\n"); + + /* This is very nearly the same thing as + + pci_save_state(pci_dev); + pci_set_power_state(pci_dev, PCI_D3hot); + pci_set_power_state(pci_dev, PCI_D0); + pci_restore_state(pci_dev); + + but we can't use these nice canned kernel routines on + kexec, because they also check the MSI/MSI-X state in PCI + configuration space and do the wrong thing when it is + set/cleared. Also, the pci_save/restore_state functions + violate the ordering requirements for restoring the + configuration space from the CCISS document (see the + comment below). So we roll our own .... */ + + for (i = 0; i < 32; i++) + pci_read_config_word(pdev, 2*i, &saved_config_space[i]); + + pos = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pos == 0) { + printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n"); + return -ENODEV; + } + + /* Quoting from the Open CISS Specification: "The Power + * Management Control/Status Register (CSR) controls the power + * state of the device. The normal operating state is D0, + * CSR=00h. The software off state is D3, CSR=03h. To reset + * the controller, place the interface device in D3 then to + * D0, this causes a secondary PCI reset which will reset the + * controller." */ + + /* enter the D3hot power management state */ + pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr); + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= PCI_D3hot; + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); + + schedule_timeout_uninterruptible(HZ >> 1); + + /* enter the D0 power management state */ + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= PCI_D0; + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); + + schedule_timeout_uninterruptible(HZ >> 1); + + /* Restore the PCI configuration space. The Open CISS + * Specification says, "Restore the PCI Configuration + * Registers, offsets 00h through 60h. It is important to + * restore the command register, 16-bits at offset 04h, + * last. Do not restore the configuration status register, + * 16-bits at offset 06h." Note that the offset is 2*i. */ + for (i = 0; i < 32; i++) { + if (i == 2 || i == 3) + continue; + pci_write_config_word(pdev, 2*i, saved_config_space[i]); + } + wmb(); + pci_write_config_word(pdev, 4, saved_config_space[2]); + + return 0; +} + /* * This is it. Find all the controllers and register them. I really hate * stealing all these major device numbers. @@ -3404,6 +3601,24 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, int dac, return_code; InquiryData_struct *inq_buff = NULL; + if (reset_devices) { + /* Reset the controller with a PCI power-cycle */ + if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev)) + return -ENODEV; + + /* Some devices (notably the HP Smart Array 5i Controller) + need a little pause here */ + schedule_timeout_uninterruptible(30*HZ); + + /* Now try to get the controller to respond to a no-op */ + for (i=0; i<12; i++) { + if (cciss_noop(pdev) == 0) + break; + else + printk("cciss: no-op failed%s\n", (i < 11 ? "; re-trying" : "")); + } + } + i = alloc_cciss_hba(); if (i < 0) return -1; -- cgit v1.2.3 From 994244883739e4044bef76d4e5d7a9b66dc6c7b6 Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy <jekhor@gmail.com> Date: Wed, 11 Feb 2009 13:25:52 -0800 Subject: s3cmci: Fix hangup in do_pio_write() This commit fixes the regression what was added by commit 088a78af978d0c8e339071a9b2bca1f4cb368f30 "s3cmci: Support transfers which are not multiple of 32 bits." fifo_free() now returns amount of available space in FIFO buffer in bytes. But do_pio_write() writes to FIFO 32-bit words. Condition for return from cycle is (fifo_free() == 0), but when fifo has 1..3 bytes of free space then this condition will never be true and system hangs. This patch changes condition in the while() to (fifo_free() > 3). Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/host/s3cmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 35a98eec7414..f4a67c65d301 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -329,7 +329,7 @@ static void do_pio_write(struct s3cmci_host *host) to_ptr = host->base + host->sdidata; - while ((fifo = fifo_free(host))) { + while ((fifo = fifo_free(host)) > 3) { if (!host->pio_bytes) { res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr); -- cgit v1.2.3 From 58a5dd3e0e77029d3db1f8fa75d0b54b38169d5d Mon Sep 17 00:00:00 2001 From: Rabin Vincent <rabin@rab.in> Date: Fri, 13 Feb 2009 22:55:26 +0530 Subject: mmc_test: fix basic read test Due to a typo in the Basic Read test, it's currently identical to the Basic Write test. Fix this. Signed-off-by: Rabin Vincent <rabin@rab.in> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/card/mmc_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index b92b172074ee..b9f1e84897cc 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -494,7 +494,7 @@ static int mmc_test_basic_read(struct mmc_test_card *test) sg_init_one(&sg, test->buffer, 512); - ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1); + ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0); if (ret) return ret; -- cgit v1.2.3 From 5dbace0c9ba110c1a3810a89fa6bf12b7574b5a3 Mon Sep 17 00:00:00 2001 From: Helmut Schaa <helmut.schaa@googlemail.com> Date: Sat, 14 Feb 2009 16:22:39 +0100 Subject: sdhci: fix led naming Fix the led device naming for the sdhci driver. The led class documentation defines the led name to have the form "devicename:colour:function" while not applicable sections should be left blank. To comply with the documentation the led device name is changed from "mmc*" to "mmc*::". Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/host/sdhci.c | 4 +++- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 24d7414a3315..f52f3053ed92 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1722,7 +1722,9 @@ int sdhci_add_host(struct sdhci_host *host) #endif #ifdef SDHCI_USE_LEDS_CLASS - host->led.name = mmc_hostname(mmc); + snprintf(host->led_name, sizeof(host->led_name), + "%s::", mmc_hostname(mmc)); + host->led.name = host->led_name; host->led.brightness = LED_OFF; host->led.default_trigger = mmc_hostname(mmc); host->led.brightness_set = sdhci_led_control; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index ffeab227d95b..ebb83657e27a 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -220,6 +220,7 @@ struct sdhci_host { #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) struct led_classdev led; /* LED control */ + char led_name[32]; #endif spinlock_t lock; /* Mutex */ -- cgit v1.2.3 From 249d0fa9d59b6165ecc224720d9ce9b7267cf1b8 Mon Sep 17 00:00:00 2001 From: David Brownell <dbrownell@users.sourceforge.net> Date: Wed, 4 Feb 2009 14:42:03 -0800 Subject: omap_hsmmc: card detect irq bugfix Work around lockdep issue when card detect IRQ handlers run in thread context ... it forces IRQF_DISABLED, which prevents all access to twl4030 card detect signals. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/host/omap_hsmmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index db37490f67ec..4dba48642e60 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -517,6 +517,9 @@ static void mmc_omap_detect(struct work_struct *work) { struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, mmc_carddetect_work); + struct omap_mmc_slot_data *slot = &mmc_slot(host); + + host->carddetect = slot->card_detect(slot->card_detect_irq); sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); if (host->carddetect) { @@ -538,7 +541,6 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) { struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; - host->carddetect = mmc_slot(host).card_detect(irq); schedule_work(&host->mmc_carddetect_work); return IRQ_HANDLED; -- cgit v1.2.3 From eb25082657be3e7639e349fc926afdcbb0a4dc65 Mon Sep 17 00:00:00 2001 From: David Brownell <dbrownell@users.sourceforge.net> Date: Tue, 17 Feb 2009 14:49:01 -0800 Subject: omap_hsmmc: only MMC1 allows HCTL.SDVS != 1.8V Based on a patch from Tony Lindgren ... after initialization, never change HCTL.SDVS except for MMC1. The other controller instances only support 1.8V in that field, although they can suport other card/SDIO/eMMC/... voltages with level shifting solutions such as external transceivers. MMC2 behavior sanity tested on Overo/WLAN, OMAP3430 SDP, and custom hardware. MMC1 also sanity tested on those platforms plus Beagle. This also fixes a bug preventing MMC2 (and also presumably MMC3) from powering down when requested. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/host/omap_hsmmc.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4dba48642e60..3bfd0facb794 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -55,6 +55,7 @@ #define VS30 (1 << 25) #define SDVS18 (0x5 << 9) #define SDVS30 (0x6 << 9) +#define SDVS33 (0x7 << 9) #define SDVSCLR 0xFFFFF1FF #define SDVSDET 0x00000400 #define AUTOIDLE 0x1 @@ -456,13 +457,20 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } /* - * Switch MMC operating voltage + * Switch MMC interface voltage ... only relevant for MMC1. + * + * MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver. + * The MMC2 transceiver controls are used instead of DAT4..DAT7. + * Some chips, like eMMC ones, use internal transceivers. */ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) { u32 reg_val = 0; int ret; + if (host->id != OMAP_MMC1_DEVID) + return 0; + /* Disable the clocks */ clk_disable(host->fclk); clk_disable(host->iclk); @@ -485,19 +493,26 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); reg_val = OMAP_HSMMC_READ(host->base, HCTL); + /* * If a MMC dual voltage card is detected, the set_ios fn calls * this fn with VDD bit set for 1.8V. Upon card removal from the * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. * - * Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is - * set in HCTL. + * Cope with a bit of slop in the range ... per data sheets: + * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max, + * but recommended values are 1.71V to 1.89V + * - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max, + * but recommended values are 2.7V to 3.3V + * + * Board setup code shouldn't permit anything very out-of-range. + * TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the + * middle range) but VSIM can't power DAT4..DAT7 at more than 3V. */ - if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) || - ((1 << vdd) == MMC_VDD_33_34))) - reg_val |= SDVS30; - if ((1 << vdd) == MMC_VDD_165_195) + if ((1 << vdd) <= MMC_VDD_23_24) reg_val |= SDVS18; + else + reg_val |= SDVS30; OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); @@ -759,10 +774,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_OFF: mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); /* - * Reset bus voltage to 3V if it got set to 1.8V earlier. + * Reset interface voltage to 3V if it's 1.8V now; + * only relevant on MMC-1, the others always use 1.8V. + * * REVISIT: If we are able to detect cards after unplugging * a 1.8V card, this code should not be needed. */ + if (host->id != OMAP_MMC1_DEVID) + break; if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { int vdd = fls(host->mmc->ocr_avail) - 1; if (omap_mmc_switch_opcond(host, vdd) != 0) @@ -786,7 +805,9 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->id == OMAP_MMC1_DEVID) { - /* Only MMC1 can operate at 3V/1.8V */ + /* Only MMC1 can interface at 3V without some flavor + * of external transceiver; but they all handle 1.8V. + */ if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && (ios->vdd == DUAL_VOLT_OCR_BIT)) { /* @@ -1139,7 +1160,9 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) " level suspend\n"); } - if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { + if (host->id == OMAP_MMC1_DEVID + && !(OMAP_HSMMC_READ(host->base, HCTL) + & SDVSDET)) { OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); -- cgit v1.2.3 From c232f457e409b34417166596ea3daf298ace95c9 Mon Sep 17 00:00:00 2001 From: Jean Pihet <jpihet@mvista.com> Date: Wed, 11 Feb 2009 13:11:39 -0800 Subject: omap_hsmmc: recover from transfer failures Timeouts during a command that has a data phase can result in the next command issued after the command that failed not being processed, i.e. no interrupt ever occurs to indicate the command has completed. This failure can result in a deadlock. This patch resets the data state machine to clear the error in case of a command timeout. Tested on OMAP3430 chip and intensive MMC/SD device removal while transferring data. Signed-off-by: Andy Lowe <alowe@mvista.com> Signed-off-by: Jean Pihet <jpihet@mvista.com> Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Acked-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/host/omap_hsmmc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 3bfd0facb794..8d21a07f63de 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -417,8 +417,15 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } end_cmd = 1; } - if (host->data) + if (host->data) { mmc_dma_cleanup(host); + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, + SYSCTL) | SRD); + while (OMAP_HSMMC_READ(host->base, + SYSCTL) & SRD) + ; + } } if ((status & DATA_TIMEOUT) || (status & DATA_CRC)) { -- cgit v1.2.3 From 3ebf74b1de9f94da4291db3ea1ae11c5bedb5784 Mon Sep 17 00:00:00 2001 From: Jean Pihet <jpihet@mvista.com> Date: Fri, 6 Feb 2009 16:42:51 +0100 Subject: omap_hsmmc: Change while(); loops with finite version Replace the infinite 'while() ;' loops with a finite loop version. Signed-off-by: Jean Pihet <jpihet@mvista.com> Acked-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> --- drivers/mmc/host/omap_hsmmc.c | 54 ++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 8d21a07f63de..a631c81dce12 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -376,6 +376,32 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) } #endif /* CONFIG_MMC_DEBUG */ +/* + * MMC controller internal state machines reset + * + * Used to reset command or data internal state machines, using respectively + * SRC or SRD bit of SYSCTL register + * Can be called from interrupt context + */ +static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host, + unsigned long bit) +{ + unsigned long i = 0; + unsigned long limit = (loops_per_jiffy * + msecs_to_jiffies(MMC_TIMEOUT_MS)); + + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) | bit); + + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && + (i++ < limit)) + cpu_relax(); + + if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) + dev_err(mmc_dev(host->mmc), + "Timeout waiting on controller reset in %s\n", + __func__); +} /* * MMC controller IRQ handler @@ -404,13 +430,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) (status & CMD_CRC)) { if (host->cmd) { if (status & CMD_TIMEOUT) { - OMAP_HSMMC_WRITE(host->base, SYSCTL, - OMAP_HSMMC_READ(host->base, - SYSCTL) | SRC); - while (OMAP_HSMMC_READ(host->base, - SYSCTL) & SRC) - ; - + mmc_omap_reset_controller_fsm(host, SRC); host->cmd->error = -ETIMEDOUT; } else { host->cmd->error = -EILSEQ; @@ -419,12 +439,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } if (host->data) { mmc_dma_cleanup(host); - OMAP_HSMMC_WRITE(host->base, SYSCTL, - OMAP_HSMMC_READ(host->base, - SYSCTL) | SRD); - while (OMAP_HSMMC_READ(host->base, - SYSCTL) & SRD) - ; + mmc_omap_reset_controller_fsm(host, SRD); } } if ((status & DATA_TIMEOUT) || @@ -434,12 +449,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) mmc_dma_cleanup(host); else host->data->error = -EILSEQ; - OMAP_HSMMC_WRITE(host->base, SYSCTL, - OMAP_HSMMC_READ(host->base, - SYSCTL) | SRD); - while (OMAP_HSMMC_READ(host->base, - SYSCTL) & SRD) - ; + mmc_omap_reset_controller_fsm(host, SRD); end_trans = 1; } } @@ -547,11 +557,7 @@ static void mmc_omap_detect(struct work_struct *work) if (host->carddetect) { mmc_detect_change(host->mmc, (HZ * 200) / 1000); } else { - OMAP_HSMMC_WRITE(host->base, SYSCTL, - OMAP_HSMMC_READ(host->base, SYSCTL) | SRD); - while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) - ; - + mmc_omap_reset_controller_fsm(host, SRD); mmc_detect_change(host->mmc, (HZ * 50) / 1000); } } -- cgit v1.2.3 From b6d6c5175809934e04a606d9193ef04924a7a7d9 Mon Sep 17 00:00:00 2001 From: Ed Cashin <ecashin@coraid.com> Date: Wed, 18 Feb 2009 14:48:13 -0800 Subject: aoe: ignore vendor extension AoE responses The Welland ME-747K-SI AoE target generates unsolicited AoE responses that are marked as vendor extensions. Instead of ignoring these packets, the aoe driver was generating kernel messages for each unrecognized response received. This patch corrects the behavior. Signed-off-by: Ed Cashin <ecashin@coraid.com> Reported-by: <karaluh@karaluh.pl> Tested-by: <karaluh@karaluh.pl> Cc: <stable@kernel.org> Cc: Alex Buell <alex.buell@munted.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/block/aoe/aoe.h | 1 + drivers/block/aoe/aoenet.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index c237527b1aa5..5e41e6dd657b 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -18,6 +18,7 @@ enum { AOECMD_ATA, AOECMD_CFG, + AOECMD_VEND_MIN = 0xf0, AOEFL_RSP = (1<<3), AOEFL_ERR = (1<<2), diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c index 30de5b1c647e..c6099ba9a4b8 100644 --- a/drivers/block/aoe/aoenet.c +++ b/drivers/block/aoe/aoenet.c @@ -142,6 +142,8 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, aoecmd_cfg_rsp(skb); break; default: + if (h->cmd >= AOECMD_VEND_MIN) + break; /* don't complain about vendor commands */ printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd); } exit: -- cgit v1.2.3 From 3a5093ee6728c8cbe9c039e685fc1fca8f965048 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan <adobriyan@gmail.com> Date: Wed, 18 Feb 2009 14:48:22 -0800 Subject: eeepc: should depend on INPUT Otherwise with INPUT=m, EEEPC_LAPTOP=y one gets drivers/built-in.o: In function `input_sync': eeepc-laptop.c:(.text+0x18ce51): undefined reference to `input_event' drivers/built-in.o: In function `input_report_key': eeepc-laptop.c:(.text+0x18ce73): undefined reference to `input_event' drivers/built-in.o: In function `eeepc_hotk_check': eeepc-laptop.c:(.text+0x18d05f): undefined reference to `input_allocate_device' eeepc-laptop.c:(.text+0x18d10f): undefined reference to `input_register_device' eeepc-laptop.c:(.text+0x18d131): undefined reference to `input_free_device' drivers/built-in.o: In function `eeepc_backlight_exit': eeepc-laptop.c:(.text+0x18d546): undefined reference to `input_unregister_device' Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Cc: Len Brown <lenb@kernel.org> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/platform/x86/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 94363115a42a..6bce56c22623 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -301,6 +301,7 @@ config INTEL_MENLOW config EEEPC_LAPTOP tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" depends on ACPI + depends on INPUT depends on EXPERIMENTAL select BACKLIGHT_CLASS_DEVICE select HWMON -- cgit v1.2.3 From ef2cfc790bf5f0ff189b01eabc0f4feb5e8524df Mon Sep 17 00:00:00 2001 From: Pavel Machek <pavel@suse.cz> Date: Wed, 18 Feb 2009 14:48:23 -0800 Subject: hp accelerometer: add freefall detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds freefall handling to hp_accel driver. According to HP, it should just work, without us having to set the chip up by hand. hpfall.c is example .c program that parks the disk when accelerometer detects free fall. It should work; for now, it uses fixed 20seconds protection period. Signed-off-by: Pavel Machek <pavel@suse.cz> Cc: Thomas Renninger <trenn@suse.de> Cc: Éric Piel <eric.piel@tremplin-utc.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- Documentation/hwmon/hpfall.c | 101 +++++++++++++++++++++++++ Documentation/hwmon/lis3lv02d | 8 ++ drivers/hwmon/hp_accel.c | 27 +++++++ drivers/hwmon/lis3lv02d.c | 172 +++++++++++++++++++++++++++++++++++++----- drivers/hwmon/lis3lv02d.h | 5 ++ 5 files changed, 296 insertions(+), 17 deletions(-) create mode 100644 Documentation/hwmon/hpfall.c (limited to 'drivers') diff --git a/Documentation/hwmon/hpfall.c b/Documentation/hwmon/hpfall.c new file mode 100644 index 000000000000..bbea1ccfd46a --- /dev/null +++ b/Documentation/hwmon/hpfall.c @@ -0,0 +1,101 @@ +/* Disk protection for HP machines. + * + * Copyright 2008 Eric Piel + * Copyright 2009 Pavel Machek <pavel@suse.cz> + * + * GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <signal.h> + +void write_int(char *path, int i) +{ + char buf[1024]; + int fd = open(path, O_RDWR); + if (fd < 0) { + perror("open"); + exit(1); + } + sprintf(buf, "%d", i); + if (write(fd, buf, strlen(buf)) != strlen(buf)) { + perror("write"); + exit(1); + } + close(fd); +} + +void set_led(int on) +{ + write_int("/sys/class/leds/hp::hddprotect/brightness", on); +} + +void protect(int seconds) +{ + write_int("/sys/block/sda/device/unload_heads", seconds*1000); +} + +int on_ac(void) +{ +// /sys/class/power_supply/AC0/online +} + +int lid_open(void) +{ +// /proc/acpi/button/lid/LID/state +} + +void ignore_me(void) +{ + protect(0); + set_led(0); + +} + +int main(int argc, char* argv[]) +{ + int fd, ret; + + fd = open("/dev/freefall", O_RDONLY); + if (fd < 0) { + perror("open"); + return EXIT_FAILURE; + } + + signal(SIGALRM, ignore_me); + + for (;;) { + unsigned char count; + + ret = read(fd, &count, sizeof(count)); + alarm(0); + if ((ret == -1) && (errno == EINTR)) { + /* Alarm expired, time to unpark the heads */ + continue; + } + + if (ret != sizeof(count)) { + perror("read"); + break; + } + + protect(21); + set_led(1); + if (1 || on_ac() || lid_open()) { + alarm(2); + } else { + alarm(20); + } + } + + close(fd); + return EXIT_SUCCESS; +} diff --git a/Documentation/hwmon/lis3lv02d b/Documentation/hwmon/lis3lv02d index 0fcfc4a7ccdc..287f8c902656 100644 --- a/Documentation/hwmon/lis3lv02d +++ b/Documentation/hwmon/lis3lv02d @@ -33,6 +33,14 @@ rate - reports the sampling rate of the accelerometer device in HZ This driver also provides an absolute input class device, allowing the laptop to act as a pinball machine-esque joystick. +Another feature of the driver is misc device called "freefall" that +acts similar to /dev/rtc and reacts on free-fall interrupts received +from the device. It supports blocking operations, poll/select and +fasync operation modes. You must read 1 bytes from the device. The +result is number of free-fall interrupts since the last successful +read (or 255 if number of interrupts would not fit). + + Axes orientation ---------------- diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index abf4dfc8ec22..dcefe1d2adbd 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -213,6 +213,30 @@ static struct delayed_led_classdev hpled_led = { .set_brightness = hpled_set, }; +static acpi_status +lis3lv02d_get_resource(struct acpi_resource *resource, void *context) +{ + if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { + struct acpi_resource_extended_irq *irq; + u32 *device_irq = context; + + irq = &resource->data.extended_irq; + *device_irq = irq->interrupts[0]; + } + + return AE_OK; +} + +static void lis3lv02d_enum_resources(struct acpi_device *device) +{ + acpi_status status; + + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + lis3lv02d_get_resource, &adev.irq); + if (ACPI_FAILURE(status)) + printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); +} + static int lis3lv02d_add(struct acpi_device *device) { u8 val; @@ -247,6 +271,9 @@ static int lis3lv02d_add(struct acpi_device *device) if (ret) return ret; + /* obtain IRQ number of our device from ACPI */ + lis3lv02d_enum_resources(adev.device); + ret = lis3lv02d_init_device(&adev); if (ret) { flush_work(&hpled_led.work); diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 219d2d0d5a62..3afa3afc77f3 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -3,7 +3,7 @@ * * Copyright (C) 2007-2008 Yan Burman * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008 Pavel Machek + * Copyright (C) 2008-2009 Pavel Machek * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ #include <linux/poll.h> #include <linux/freezer.h> #include <linux/uaccess.h> +#include <linux/miscdevice.h> #include <acpi/acpi_drivers.h> #include <asm/atomic.h> #include "lis3lv02d.h" @@ -55,7 +56,10 @@ /* Maximum value our axis may get for the input device (signed 12 bits) */ #define MDPS_MAX_VAL 2048 -struct acpi_lis3lv02d adev; +struct acpi_lis3lv02d adev = { + .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(adev.misc_wait), +}; + EXPORT_SYMBOL_GPL(adev); static int lis3lv02d_add_fs(struct acpi_device *device); @@ -110,26 +114,13 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) void lis3lv02d_poweroff(acpi_handle handle) { adev.is_on = 0; - /* disable X,Y,Z axis and power down */ - adev.write(handle, CTRL_REG1, 0x00); } EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); void lis3lv02d_poweron(acpi_handle handle) { - u8 val; - adev.is_on = 1; adev.init(handle); - adev.write(handle, FF_WU_CFG, 0); - /* - * BDU: LSB and MSB values are not updated until both have been read. - * So the value read will always be correct. - * IEN: Interrupt for free-fall and DD, not for data-ready. - */ - adev.read(handle, CTRL_REG2, &val); - val |= CTRL2_BDU | CTRL2_IEN; - adev.write(handle, CTRL_REG2, val); } EXPORT_SYMBOL_GPL(lis3lv02d_poweron); @@ -162,6 +153,140 @@ static void lis3lv02d_decrease_use(struct acpi_lis3lv02d *dev) mutex_unlock(&dev->lock); } +static irqreturn_t lis302dl_interrupt(int irq, void *dummy) +{ + /* + * Be careful: on some HP laptops the bios force DD when on battery and + * the lid is closed. This leads to interrupts as soon as a little move + * is done. + */ + atomic_inc(&adev.count); + + wake_up_interruptible(&adev.misc_wait); + kill_fasync(&adev.async_queue, SIGIO, POLL_IN); + return IRQ_HANDLED; +} + +static int lis3lv02d_misc_open(struct inode *inode, struct file *file) +{ + int ret; + + if (test_and_set_bit(0, &adev.misc_opened)) + return -EBUSY; /* already open */ + + atomic_set(&adev.count, 0); + + /* + * The sensor can generate interrupts for free-fall and direction + * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep + * the things simple and _fast_ we activate it only for free-fall, so + * no need to read register (very slow with ACPI). For the same reason, + * we forbid shared interrupts. + * + * IRQF_TRIGGER_RISING seems pointless on HP laptops because the + * io-apic is not configurable (and generates a warning) but I keep it + * in case of support for other hardware. + */ + ret = request_irq(adev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING, + DRIVER_NAME, &adev); + + if (ret) { + clear_bit(0, &adev.misc_opened); + printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", adev.irq); + return -EBUSY; + } + lis3lv02d_increase_use(&adev); + printk("lis3: registered interrupt %d\n", adev.irq); + return 0; +} + +static int lis3lv02d_misc_release(struct inode *inode, struct file *file) +{ + fasync_helper(-1, file, 0, &adev.async_queue); + lis3lv02d_decrease_use(&adev); + free_irq(adev.irq, &adev); + clear_bit(0, &adev.misc_opened); /* release the device */ + return 0; +} + +static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + DECLARE_WAITQUEUE(wait, current); + u32 data; + unsigned char byte_data; + ssize_t retval = 1; + + if (count < 1) + return -EINVAL; + + add_wait_queue(&adev.misc_wait, &wait); + while (true) { + set_current_state(TASK_INTERRUPTIBLE); + data = atomic_xchg(&adev.count, 0); + if (data) + break; + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + if (data < 255) + byte_data = data; + else + byte_data = 255; + + /* make sure we are not going into copy_to_user() with + * TASK_INTERRUPTIBLE state */ + set_current_state(TASK_RUNNING); + if (copy_to_user(buf, &byte_data, sizeof(byte_data))) + retval = -EFAULT; + +out: + __set_current_state(TASK_RUNNING); + remove_wait_queue(&adev.misc_wait, &wait); + + return retval; +} + +static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &adev.misc_wait, wait); + if (atomic_read(&adev.count)) + return POLLIN | POLLRDNORM; + return 0; +} + +static int lis3lv02d_misc_fasync(int fd, struct file *file, int on) +{ + return fasync_helper(fd, file, on, &adev.async_queue); +} + +static const struct file_operations lis3lv02d_misc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = lis3lv02d_misc_read, + .open = lis3lv02d_misc_open, + .release = lis3lv02d_misc_release, + .poll = lis3lv02d_misc_poll, + .fasync = lis3lv02d_misc_fasync, +}; + +static struct miscdevice lis3lv02d_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "freefall", + .fops = &lis3lv02d_misc_fops, +}; + /** * lis3lv02d_joystick_kthread - Kthread polling function * @data: unused - here to conform to threadfn prototype @@ -203,7 +328,6 @@ static void lis3lv02d_joystick_close(struct input_dev *input) lis3lv02d_decrease_use(&adev); } - static inline void lis3lv02d_calibrate_joystick(void) { lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); @@ -250,6 +374,7 @@ void lis3lv02d_joystick_disable(void) if (!adev.idev) return; + misc_deregister(&lis3lv02d_misc_device); input_unregister_device(adev.idev); adev.idev = NULL; } @@ -268,6 +393,19 @@ int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) if (lis3lv02d_joystick_enable()) printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); + printk("lis3_init_device: irq %d\n", dev->irq); + + /* if we did not get an IRQ from ACPI - we have nothing more to do */ + if (!dev->irq) { + printk(KERN_ERR DRIVER_NAME + ": No IRQ in ACPI. Disabling /dev/freefall\n"); + goto out; + } + + printk("lis3: registering device\n"); + if (misc_register(&lis3lv02d_misc_device)) + printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); +out: lis3lv02d_decrease_use(dev); return 0; } @@ -351,6 +489,6 @@ int lis3lv02d_remove_fs(void) EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); -MODULE_AUTHOR("Yan Burman and Eric Piel"); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 223f1c0763bb..2e7597c42d80 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -170,6 +170,11 @@ struct acpi_lis3lv02d { unsigned char is_on; /* whether the device is on or off */ unsigned char usage; /* usage counter */ struct axis_conversion ac; /* hw -> logical axis */ + + u32 irq; /* IRQ number */ + struct fasync_struct *async_queue; /* queue for the misc device */ + wait_queue_head_t misc_wait; /* Wait queue for the misc device */ + unsigned long misc_opened; /* bit0: whether the device is open */ }; int lis3lv02d_init_device(struct acpi_lis3lv02d *dev); -- cgit v1.2.3 From 137bad32342a613586347341d1307c2b9812ef44 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta <giuseppe.bilotta@gmail.com> Date: Wed, 18 Feb 2009 14:48:24 -0800 Subject: lis3lv02d: support both one- and two-byte sensors Sensors responding with 0x3B to WHO_AM_I only have one data register per direction, thus returning a signed byte from the position which is occupied by the MSB in sensors responding with 0x3A. Since multiple sensors share the reply to WHO_AM_I, we rename the defines to better indicate what they identify (family of single and double precision sensors). We support both kind of sensors by checking for the sensor type on init and defining appropriate data-access routines and sensor limits (for the joystick) depending on what we find. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com> Acked-by: Eric Piel <Eric.Piel@tremplin-utc.net> Cc: Pavel Machek <pavel@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/hwmon/hp_accel.c | 36 ++++++++++++++++++++++++++++++++---- drivers/hwmon/lis3lv02d.c | 25 ++++++------------------- drivers/hwmon/lis3lv02d.h | 16 +++++++++++++--- 3 files changed, 51 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index dcefe1d2adbd..6b16566c4e6c 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -237,9 +237,25 @@ static void lis3lv02d_enum_resources(struct acpi_device *device) printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); } +static s16 lis3lv02d_read_16(acpi_handle handle, int reg) +{ + u8 lo, hi; + + adev.read(handle, reg - 1, &lo); + adev.read(handle, reg, &hi); + /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ + return (s16)((hi << 8) | lo); +} + +static s16 lis3lv02d_read_8(acpi_handle handle, int reg) +{ + s8 lo; + adev.read(handle, reg, &lo); + return lo; +} + static int lis3lv02d_add(struct acpi_device *device) { - u8 val; int ret; if (!device) @@ -253,10 +269,22 @@ static int lis3lv02d_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); device->driver_data = &adev; - lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); - if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { + lis3lv02d_acpi_read(device->handle, WHO_AM_I, &adev.whoami); + switch (adev.whoami) { + case LIS_DOUBLE_ID: + printk(KERN_INFO DRIVER_NAME ": 2-byte sensor found\n"); + adev.read_data = lis3lv02d_read_16; + adev.mdps_max_val = 2048; + break; + case LIS_SINGLE_ID: + printk(KERN_INFO DRIVER_NAME ": 1-byte sensor found\n"); + adev.read_data = lis3lv02d_read_8; + adev.mdps_max_val = 128; + break; + default: printk(KERN_ERR DRIVER_NAME - ": Accelerometer chip not LIS3LV02D{L,Q}\n"); + ": unknown sensor type 0x%X\n", adev.whoami); + return -EINVAL; } /* If possible use a "standard" axes order */ diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 3afa3afc77f3..8bb2158f0453 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -53,9 +53,6 @@ * joystick. */ -/* Maximum value our axis may get for the input device (signed 12 bits) */ -#define MDPS_MAX_VAL 2048 - struct acpi_lis3lv02d adev = { .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(adev.misc_wait), }; @@ -64,16 +61,6 @@ EXPORT_SYMBOL_GPL(adev); static int lis3lv02d_add_fs(struct acpi_device *device); -static s16 lis3lv02d_read_16(acpi_handle handle, int reg) -{ - u8 lo, hi; - - adev.read(handle, reg, &lo); - adev.read(handle, reg + 1, &hi); - /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ - return (s16)((hi << 8) | lo); -} - /** * lis3lv02d_get_axis - For the given axis, give the value converted * @axis: 1,2,3 - can also be negative @@ -102,9 +89,9 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) { int position[3]; - position[0] = lis3lv02d_read_16(handle, OUTX_L); - position[1] = lis3lv02d_read_16(handle, OUTY_L); - position[2] = lis3lv02d_read_16(handle, OUTZ_L); + position[0] = adev.read_data(handle, OUTX); + position[1] = adev.read_data(handle, OUTY); + position[2] = adev.read_data(handle, OUTZ); *x = lis3lv02d_get_axis(adev.ac.x, position); *y = lis3lv02d_get_axis(adev.ac.y, position); @@ -355,9 +342,9 @@ int lis3lv02d_joystick_enable(void) adev.idev->close = lis3lv02d_joystick_close; set_bit(EV_ABS, adev.idev->evbit); - input_set_abs_params(adev.idev, ABS_X, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); - input_set_abs_params(adev.idev, ABS_Y, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); - input_set_abs_params(adev.idev, ABS_Z, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + input_set_abs_params(adev.idev, ABS_X, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); + input_set_abs_params(adev.idev, ABS_Y, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); + input_set_abs_params(adev.idev, ABS_Z, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); err = input_register_device(adev.idev); if (err) { diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 2e7597c42d80..75972bf372ff 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -22,12 +22,15 @@ /* * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to * be connected via SPI. There exists also several similar chips (such as LIS302DL or - * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. + * LIS3L02DQ) and they have slightly different registers, but we can provide a + * common interface for all of them. * They can also be connected via I²C. */ -#define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ -#define LIS302DL_ID 0x3B /* Also the LIS202DL! */ +/* 2-byte registers */ +#define LIS_DOUBLE_ID 0x3A /* LIS3LV02D[LQ] */ +/* 1-byte registers */ +#define LIS_SINGLE_ID 0x3B /* LIS[32]02DL and others */ enum lis3lv02d_reg { WHO_AM_I = 0x0F, @@ -44,10 +47,13 @@ enum lis3lv02d_reg { STATUS_REG = 0x27, OUTX_L = 0x28, OUTX_H = 0x29, + OUTX = 0x29, OUTY_L = 0x2A, OUTY_H = 0x2B, + OUTY = 0x2B, OUTZ_L = 0x2C, OUTZ_H = 0x2D, + OUTZ = 0x2D, FF_WU_CFG = 0x30, FF_WU_SRC = 0x31, FF_WU_ACK = 0x32, @@ -159,6 +165,10 @@ struct acpi_lis3lv02d { acpi_status (*write) (acpi_handle handle, int reg, u8 val); acpi_status (*read) (acpi_handle handle, int reg, u8 *ret); + u8 whoami; /* 3Ah: 2-byte registries, 3Bh: 1-byte registries */ + s16 (*read_data) (acpi_handle handle, int reg); + int mdps_max_val; + struct input_dev *idev; /* input device */ struct task_struct *kthread; /* kthread for input */ struct mutex lock; -- cgit v1.2.3 From 9ccf3b5e8409927835c4d38cb2f380c9e4349e76 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta <giuseppe.bilotta@gmail.com> Date: Wed, 18 Feb 2009 14:48:25 -0800 Subject: lis3lv02d: add axes knowledge of HP Pavilion dv5 models Add support for HP Pavilion dv5. Since Intel-based models have an inverted x axis, while AMD-based models have an inverted y axis, we introduce a new macro that special-cases axis orientation based on two DMI entries: HP dv5 axis configuration is then based on both the PRODUCT and BOARD name. Signed-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com> Cc: Eric Piel <Eric.Piel@tremplin-utc.net> Cc: Pavel Machek <pavel@suse.cz> Tested-by: Palatis Tseng <palatis@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/hwmon/hp_accel.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 6b16566c4e6c..29c83b5b9697 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -166,6 +166,18 @@ static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3}; }, \ .driver_data = &lis3lv02d_axis_##_axis \ } + +#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ + _class2, _name2, \ + _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_##_class1, _name1), \ + DMI_MATCH(DMI_##_class2, _name2), \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} static struct dmi_system_id lis3lv02d_dmi_ids[] = { /* product names are truncated to match all kinds of a same model */ AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), @@ -179,6 +191,16 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("NC673x", "HP Compaq 673", xy_rotated_left_usd), AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), AXIS_DMI_MATCH("NC671xx", "HP Compaq 671", xy_swap_yz_inverted), + /* Intel-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_I", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3603", + x_inverted), + /* AMD-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_A", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3600", + y_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" -- cgit v1.2.3 From 287d859222e0adbc67666a6154aaf42d7d5bbb54 Mon Sep 17 00:00:00 2001 From: Dan Williams <dan.j.williams@intel.com> Date: Wed, 18 Feb 2009 14:48:26 -0800 Subject: atmel-mci: fix initialization of dma slave data The conversion of atmel-mci to dma_request_channel missed the initialization of the channel dma_slave information. The filter_fn passed to dma_request_channel is responsible for initializing the channel's private data. This implementation has the additional benefit of enabling a generic client-channel data passing mechanism. Reviewed-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Acked-by: Haavard Skinnemoen <hskinnemoen@atmel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/dma/dmaengine.c | 2 ++ drivers/dma/dw_dmac.c | 5 ++--- drivers/dma/dw_dmac_regs.h | 2 -- drivers/mmc/host/atmel-mci.c | 5 +++-- include/linux/dmaengine.h | 2 ++ 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a58993011edb..280a9d263eb3 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -518,6 +518,7 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v dma_chan_name(chan), err); else break; + chan->private = NULL; chan = NULL; } } @@ -536,6 +537,7 @@ void dma_release_channel(struct dma_chan *chan) WARN_ONCE(chan->client_count != 1, "chan reference count %d != 1\n", chan->client_count); dma_chan_put(chan); + chan->private = NULL; mutex_unlock(&dma_list_mutex); } EXPORT_SYMBOL_GPL(dma_release_channel); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6b702cc46b3d..a97c07eef7ec 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -560,7 +560,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = dwc->dws; + struct dw_dma_slave *dws = chan->private; struct dw_desc *prev; struct dw_desc *first; u32 ctllo; @@ -790,7 +790,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) cfghi = DWC_CFGH_FIFO_MODE; cfglo = 0; - dws = dwc->dws; + dws = chan->private; if (dws) { /* * We need controller-specific data to set up slave @@ -866,7 +866,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) spin_lock_bh(&dwc->lock); list_splice_init(&dwc->free_list, &list); dwc->descs_allocated = 0; - dwc->dws = NULL; /* Disable interrupts */ channel_clear_bit(dw, MASK.XFER, dwc->mask); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 00fdd187bb0c..b252b202c5cf 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -139,8 +139,6 @@ struct dw_dma_chan { struct list_head queue; struct list_head free_list; - struct dw_dma_slave *dws; - unsigned int descs_allocated; }; diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 76bfe16c09b1..2b1196e6142c 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1548,9 +1548,10 @@ static bool filter(struct dma_chan *chan, void *slave) { struct dw_dma_slave *dws = slave; - if (dws->dma_dev == chan->device->dev) + if (dws->dma_dev == chan->device->dev) { + chan->private = dws; return true; - else + } else return false; } #endif diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 3e68469c1885..f0413845f20e 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -121,6 +121,7 @@ struct dma_chan_percpu { * @local: per-cpu pointer to a struct dma_chan_percpu * @client-count: how many clients are using this channel * @table_count: number of appearances in the mem-to-mem allocation table + * @private: private data for certain client-channel associations */ struct dma_chan { struct dma_device *device; @@ -134,6 +135,7 @@ struct dma_chan { struct dma_chan_percpu *local; int client_count; int table_count; + void *private; }; /** -- cgit v1.2.3 From 27c0c8e511fa9e2389503926840fac606d90a049 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Date: Wed, 18 Feb 2009 14:48:28 -0800 Subject: atmel_serial might lose modem status change I found a problem of handling of modem status of atmel_serial driver. With the commit 1ecc26 ("atmel_serial: split the interrupt handler"), handling of modem status signal was splitted into two parts. The atmel_tasklet_func() compares new status with irq_status_prev, but irq_status_prev is not correct if signal status was changed while the port is closed. Here is a sequence to cause problem: 1. Remote side sets CTS (and DSR). 2. Local side close the port. 3. Local side clears RTS and DTR. 4. Remote side clears CTS and DSR. 5. Local side reopen the port. hw_stopped becomes 1. 6. Local side sets RTS and DTR. 7. Remote side sets CTS and DSR. Then CTS change interrupt can be received, but since CTS bit in irq_status_prev and new status is same, uart_handle_cts_change() will not be called (so hw_stopped will not be cleared, i.e. cannot send any data). I suppose irq_status_prev should be initialized at somewhere in open sequence. Itai Levi pointed out that we need to initialize atmel_port->irq_status as well here. His analysis is as follows: > Regarding the second part of the patch (which resets irq_status_prev), > it turns out that both versions of the patch (mine and Atsushi's) > still leave enough room for faulty behavior when opening the port. > > This is because we are not resetting both irq_status_prev and > irq_status in atmel_startup() to CSR, which leads faulty behavior in > the following sequences: > > First case: > 1. closing the port while CTS line = 1 (TX not allowed) > 2. setting CTS line = 0 (TX allowed) > 3. opening the port > 4. transmitting one char > 5. Cannot transmit more chars, although CTS line is 0 > > Second case: > 1. closing the port while CTS line = 0 (TX allowed) > 2. setting CTS line = 1 (TX not allowed) > 3. opening the port > 4. receiving some chars > 5. Now we can transmit, although CTS line is 1 > > This reason for this is that the tasklet is scheduled as a result of > TX or RX interrupts (not a status change!), in steps 4 above. Inside > the tasklet, the atmel_port->irq_status (which holds the value from > the previous session) is compared to atmel_port->irq_status_prev. > Hence, a status-change of the CTS line is faultily detected. > > Both cases were verified on 9260 hardware. [haavard.skinnemoen@atmel.com: folded with patch from Itai Levi] Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com> Cc: Remy Bohmer <linux@bohmer.net> Cc: Marc Pignat <marc.pignat@hevs.ch> Cc: Itai Levi <itai.levi.devel@gmail.com> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/serial/atmel_serial.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 89362d733d62..8f58f7ff0dd7 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -877,6 +877,10 @@ static int atmel_startup(struct uart_port *port) } } + /* Save current CSR for comparison in atmel_tasklet_func() */ + atmel_port->irq_status_prev = UART_GET_CSR(port); + atmel_port->irq_status = atmel_port->irq_status_prev; + /* * Finally, enable the serial port */ -- cgit v1.2.3 From ffa7525c13eb3db0fd19a3e1cffe2ce6f561f5f3 Mon Sep 17 00:00:00 2001 From: Adam Lackorzynski <adam@os.inf.tu-dresden.de> Date: Wed, 18 Feb 2009 14:48:34 -0800 Subject: jsm: additional device support I have a Digi Neo 8 PCI card (114f:00b1) Serial controller: Digi International Digi Neo 8 (rev 05) that works with the jsm driver after using the following patch. Signed-off-by: Adam Lackorzynski <adam@os.inf.tu-dresden.de> Cc: Scott H Kilau <Scott_Kilau@digi.com> Cc: Wendy Xiong <wendyx@us.ibm.com> Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/serial/jsm/jsm_driver.c | 3 +++ include/linux/pci_ids.h | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index 92187e28608a..ac79cbe4c2cf 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -84,6 +84,8 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) brd->pci_dev = pdev; if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM) brd->maxports = 4; + else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8) + brd->maxports = 8; else brd->maxports = 2; @@ -212,6 +214,7 @@ static struct pci_device_id jsm_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 }, { 0, } }; MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 918391b4b109..114b8192eab9 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1445,6 +1445,7 @@ #define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 #define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 #define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 +#define PCI_DEVICE_ID_DIGI_NEO_8 0x00B1 #define PCI_DEVICE_ID_NEO_2DB9 0x00C8 #define PCI_DEVICE_ID_NEO_2DB9PRI 0x00C9 #define PCI_DEVICE_ID_NEO_2RJ45 0x00CA -- cgit v1.2.3 From 5a74db06cc8d36a325913aa4968ae169f997a466 Mon Sep 17 00:00:00 2001 From: Philippe De Muyter <phdm@macqel.be> Date: Wed, 18 Feb 2009 14:48:36 -0800 Subject: floppy: request and release only the ports we actually use The floppy driver requests an I/O port it doesn't need, and sometimes this causes a conflict with a motherboard device reported by PNPBIOS. This patch makes the floppy driver request and release only the ports it actually uses. It also factors out the request/release stuff and the io-ports list so they're all in one place now. The current floppy driver uses only these ports: 0x3f2 (FD_DOR) 0x3f4 (FD_STATUS) 0x3f5 (FD_DATA) 0x3f7 (FD_DCR/FD_DIR) but it requests 0x3f2-0x3f5 and 0x3f7, which includes the unused port 0x3f3. Some BIOSes report 0x3f3 as a motherboard resource. The PNP system driver reserves that, which causes a conflict when the floppy driver requests 0x3f2-0x3f5 later. Philippe reported that this conflict broke the floppy driver between 2.6.11 and 2.6.22. His PNPBIOS reports these devices: $ cat 00:07/id 00:07/resources # motherboard device PNP0c02 state = active io 0x80-0x80 io 0x10-0x1f io 0x22-0x3f io 0x44-0x5f io 0x90-0x9f io 0xa2-0xbf io 0x3f0-0x3f1 io 0x3f3-0x3f3 $ cat 00:03/id 00:03/resources # floppy device PNP0700 state = active io 0x3f4-0x3f5 io 0x3f2-0x3f2 Reference: http://lkml.org/lkml/2009/1/31/162 Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Philippe De Muyter <phdm@macqel.be> Reported-by: Philippe De Muyter <phdm@macqel.be> Tested-by: Philippe De Muyter <phdm@macqel.be> Cc: Adam M Belay <abelay@mit.edu> Cc: Robert Hancock <hancockrwd@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/block/floppy.c | 79 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index cf29cc4e6ab7..83d8ed39433d 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -558,6 +558,8 @@ static void process_fd_request(void); static void recalibrate_floppy(void); static void floppy_shutdown(unsigned long); +static int floppy_request_regions(int); +static void floppy_release_regions(int); static int floppy_grab_irq_and_dma(void); static void floppy_release_irq_and_dma(void); @@ -4274,8 +4276,7 @@ static int __init floppy_init(void) FDCS->rawcmd = 2; if (user_reset_fdc(-1, FD_RESET_ALWAYS, 0)) { /* free ioports reserved by floppy_grab_irq_and_dma() */ - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); + floppy_release_regions(fdc); FDCS->address = -1; FDCS->version = FDC_NONE; continue; @@ -4284,8 +4285,7 @@ static int __init floppy_init(void) FDCS->version = get_fdc_version(); if (FDCS->version == FDC_NONE) { /* free ioports reserved by floppy_grab_irq_and_dma() */ - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); + floppy_release_regions(fdc); FDCS->address = -1; continue; } @@ -4358,6 +4358,47 @@ out_put_disk: static DEFINE_SPINLOCK(floppy_usage_lock); +static const struct io_region { + int offset; + int size; +} io_regions[] = { + { 2, 1 }, + /* address + 3 is sometimes reserved by pnp bios for motherboard */ + { 4, 2 }, + /* address + 6 is reserved, and may be taken by IDE. + * Unfortunately, Adaptec doesn't know this :-(, */ + { 7, 1 }, +}; + +static void floppy_release_allocated_regions(int fdc, const struct io_region *p) +{ + while (p != io_regions) { + p--; + release_region(FDCS->address + p->offset, p->size); + } +} + +#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)])) + +static int floppy_request_regions(int fdc) +{ + const struct io_region *p; + + for (p = io_regions; p < ARRAY_END(io_regions); p++) { + if (!request_region(FDCS->address + p->offset, p->size, "floppy")) { + DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address + p->offset); + floppy_release_allocated_regions(fdc, p); + return -EBUSY; + } + } + return 0; +} + +static void floppy_release_regions(int fdc) +{ + floppy_release_allocated_regions(fdc, ARRAY_END(io_regions)); +} + static int floppy_grab_irq_and_dma(void) { unsigned long flags; @@ -4399,18 +4440,8 @@ static int floppy_grab_irq_and_dma(void) for (fdc = 0; fdc < N_FDC; fdc++) { if (FDCS->address != -1) { - if (!request_region(FDCS->address + 2, 4, "floppy")) { - DPRINT("Floppy io-port 0x%04lx in use\n", - FDCS->address + 2); - goto cleanup1; - } - if (!request_region(FDCS->address + 7, 1, "floppy DIR")) { - DPRINT("Floppy io-port 0x%04lx in use\n", - FDCS->address + 7); - goto cleanup2; - } - /* address + 6 is reserved, and may be taken by IDE. - * Unfortunately, Adaptec doesn't know this :-(, */ + if (floppy_request_regions(fdc)) + goto cleanup; } } for (fdc = 0; fdc < N_FDC; fdc++) { @@ -4432,15 +4463,11 @@ static int floppy_grab_irq_and_dma(void) fdc = 0; irqdma_allocated = 1; return 0; -cleanup2: - release_region(FDCS->address + 2, 4); -cleanup1: +cleanup: fd_free_irq(); fd_free_dma(); - while (--fdc >= 0) { - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); - } + while (--fdc >= 0) + floppy_release_regions(fdc); spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4501,10 +4528,8 @@ static void floppy_release_irq_and_dma(void) #endif old_fdc = fdc; for (fdc = 0; fdc < N_FDC; fdc++) - if (FDCS->address != -1) { - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); - } + if (FDCS->address != -1) + floppy_release_regions(fdc); fdc = old_fdc; } -- cgit v1.2.3 From a1a5c3b9237662f326cc730e167e7524b5d05a36 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt <krzysztof.h1@wp.pl> Date: Wed, 18 Feb 2009 14:48:38 -0800 Subject: fbdev/drm: fix Kconfig submenu mess in "Graphics support" Submenus of the graphics support "Support for frame buffer devices" and "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" are broken in half after latest changes for Intel 915 mode setting support. The DRM subsection is broken because one option is put outside the choice section it depends on. The frame buffers part is broken then due to circular dependency. Fix this by make Intel frame buffers depend on CONFIG_INTEL_AGP. Kconfigs are broken by d2f59357700487a8b944f4f7777d1e97cf5ea2ed ("drm/i915: select framebuffer support automatically"). This is probably not only way to fix this. Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl> Cc: Ingo Molnar <mingo@elte.hu> Cc: Dave Airlie <airlied@linux.ie> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/gpu/drm/Kconfig | 13 ++++++------- drivers/video/Kconfig | 10 ++-------- 2 files changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 4be3acbaaf9a..3a22eb9be378 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -80,18 +80,17 @@ config DRM_I915 XFree86 4.4 and above. If unsure, build this and i830 as modules and the X server will load the correct one. -endchoice - config DRM_I915_KMS bool "Enable modesetting on intel by default" depends on DRM_I915 help - Choose this option if you want kernel modesetting enabled by default, - and you have a new enough userspace to support this. Running old - userspaces with this enabled will cause pain. Note that this causes - the driver to bind to PCI devices, which precludes loading things - like intelfb. + Choose this option if you want kernel modesetting enabled by default, + and you have a new enough userspace to support this. Running old + userspaces with this enabled will cause pain. Note that this causes + the driver to bind to PCI devices, which precludes loading things + like intelfb. +endchoice config DRM_MGA tristate "Matrox g200/g400" diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index bf0af660df8a..fb19803060cf 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1054,10 +1054,7 @@ config FB_RIVA_BACKLIGHT config FB_I810 tristate "Intel 810/815 support (EXPERIMENTAL)" - depends on EXPERIMENTAL && PCI && X86_32 - select AGP - select AGP_INTEL - select FB + depends on EXPERIMENTAL && FB && PCI && X86_32 && AGP_INTEL select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1120,10 +1117,7 @@ config FB_CARILLO_RANCH config FB_INTEL tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support (EXPERIMENTAL)" - depends on EXPERIMENTAL && PCI && X86 - select FB - select AGP - select AGP_INTEL + depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA -- cgit v1.2.3 From 310d8c93f9f07499cd7ca82d7997774a89de00e7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap <randy.dunlap@oracle.com> Date: Wed, 18 Feb 2009 14:48:39 -0800 Subject: x86: dell-laptop: depends on POWER_SUPPLY Build breaks when DELL_LAPTOP=y and POWER_SUPPLY=m. DELL_LAPTOP needs to depend on POWER_SUPPLY. dell-laptop.c:(.text+0x1ef3c4): undefined reference to `power_supply_is_system_supplied' dell-laptop.c:(.text+0x1ef45e): undefined reference to `power_supply_is_system_supplied' Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> Cc: Matthew Garrett <mjg59@srcf.ucam.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Len Brown <lenb@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/platform/x86/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 6bce56c22623..b3866ad50227 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -62,6 +62,7 @@ config DELL_LAPTOP depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE depends on RFKILL + depends on POWER_SUPPLY default n ---help--- This driver adds support for rfkill and backlight control to Dell -- cgit v1.2.3 From 97bef7dd05563807539122c488a5dd93ed327722 Mon Sep 17 00:00:00 2001 From: Bernhard Walle <bernhard.walle@gmx.de> Date: Wed, 18 Feb 2009 14:48:40 -0800 Subject: Bernhard has moved Since I don't work for SUSE any more and the bwalle@suse.de address is invalid, correct it in the copyright headers and documentation. Signed-off-by: Bernhard Walle <bernhard.walle@gmx.de> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- Documentation/ABI/testing/sysfs-firmware-memmap | 2 +- drivers/firmware/memmap.c | 2 +- include/linux/firmware-map.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-firmware-memmap b/Documentation/ABI/testing/sysfs-firmware-memmap index 0d99ee6ae02e..eca0d65087dc 100644 --- a/Documentation/ABI/testing/sysfs-firmware-memmap +++ b/Documentation/ABI/testing/sysfs-firmware-memmap @@ -1,6 +1,6 @@ What: /sys/firmware/memmap/ Date: June 2008 -Contact: Bernhard Walle <bwalle@suse.de> +Contact: Bernhard Walle <bernhard.walle@gmx.de> Description: On all platforms, the firmware provides a memory map which the kernel reads. The resources from that memory map are registered diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index 261b9aa3f248..05aa2d406ac6 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -1,7 +1,7 @@ /* * linux/drivers/firmware/memmap.c * Copyright (C) 2008 SUSE LINUX Products GmbH - * by Bernhard Walle <bwalle@suse.de> + * by Bernhard Walle <bernhard.walle@gmx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 as published by diff --git a/include/linux/firmware-map.h b/include/linux/firmware-map.h index 6e199c8dfacc..cca686b39123 100644 --- a/include/linux/firmware-map.h +++ b/include/linux/firmware-map.h @@ -1,7 +1,7 @@ /* * include/linux/firmware-map.h: * Copyright (C) 2008 SUSE LINUX Products GmbH - * by Bernhard Walle <bwalle@suse.de> + * by Bernhard Walle <bernhard.walle@gmx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 as published by -- cgit v1.2.3 From be50344e604f956891fc0013f1ba78823a758124 Mon Sep 17 00:00:00 2001 From: Michael Buesch <mb@bu3sch.de> Date: Wed, 18 Feb 2009 14:48:41 -0800 Subject: spi-gpio: sanitize MISO bitvalue gpio_get_value() returns 0 or nonzero, but getmiso() expects 0 or 1. Sanitize the value to a 0/1 boolean. Signed-off-by: Michael Buesch <mb@bu3sch.de> Acked-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/spi/spi_gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi_gpio.c b/drivers/spi/spi_gpio.c index 49698cabc30d..f5ed9721aabb 100644 --- a/drivers/spi/spi_gpio.c +++ b/drivers/spi/spi_gpio.c @@ -114,7 +114,7 @@ static inline void setmosi(const struct spi_device *spi, int is_on) static inline int getmiso(const struct spi_device *spi) { - return gpio_get_value(SPI_MISO_GPIO); + return !!gpio_get_value(SPI_MISO_GPIO); } #undef pdata -- cgit v1.2.3 From 43250ddd75a35d1f7926d989a10fefd30c37eaa7 Mon Sep 17 00:00:00 2001 From: Jie Yang <jie.yang@atheros.com> Date: Wed, 18 Feb 2009 17:24:15 -0800 Subject: atl1c: Atheros L1C Gigabit Ethernet driver Supporting AR8131, and AR8132. Signed-off-by: Jie Yang <jie.yang@atheros.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/Kconfig | 11 + drivers/net/Makefile | 1 + drivers/net/atl1c/Makefile | 2 + drivers/net/atl1c/atl1c.h | 606 ++++++++ drivers/net/atl1c/atl1c_ethtool.c | 317 +++++ drivers/net/atl1c/atl1c_hw.c | 527 +++++++ drivers/net/atl1c/atl1c_hw.h | 859 ++++++++++++ drivers/net/atl1c/atl1c_main.c | 2797 +++++++++++++++++++++++++++++++++++++ 8 files changed, 5120 insertions(+) create mode 100644 drivers/net/atl1c/Makefile create mode 100644 drivers/net/atl1c/atl1c.h create mode 100644 drivers/net/atl1c/atl1c_ethtool.c create mode 100644 drivers/net/atl1c/atl1c_hw.c create mode 100644 drivers/net/atl1c/atl1c_hw.h create mode 100644 drivers/net/atl1c/atl1c_main.c (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 6bdfd47d679d..a2f185fd7072 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2342,6 +2342,17 @@ config ATL1E To compile this driver as a module, choose M here. The module will be called atl1e. +config ATL1C + tristate "Atheros L1C Gigabit Ethernet support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + select MII + help + This driver supports the Atheros L1C gigabit ethernet adapter. + + To compile this driver as a module, choose M here. The module + will be called atl1c. + config JME tristate "JMicron(R) PCI-Express Gigabit Ethernet support" depends on PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a3c5c002f224..aca8492db654 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_BONDING) += bonding/ obj-$(CONFIG_ATL1) += atlx/ obj-$(CONFIG_ATL2) += atlx/ obj-$(CONFIG_ATL1E) += atl1e/ +obj-$(CONFIG_ATL1C) += atl1c/ obj-$(CONFIG_GIANFAR) += gianfar_driver.o obj-$(CONFIG_TEHUTI) += tehuti.o obj-$(CONFIG_ENIC) += enic/ diff --git a/drivers/net/atl1c/Makefile b/drivers/net/atl1c/Makefile new file mode 100644 index 000000000000..c37d966952ee --- /dev/null +++ b/drivers/net/atl1c/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ATL1C) += atl1c.o +atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h new file mode 100644 index 000000000000..ac11b84b8377 --- /dev/null +++ b/drivers/net/atl1c/atl1c.h @@ -0,0 +1,606 @@ +/* + * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ATL1C_H_ +#define _ATL1C_H_ + +#include <linux/version.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/udp.h> +#include <linux/mii.h> +#include <linux/io.h> +#include <linux/vmalloc.h> +#include <linux/pagemap.h> +#include <linux/tcp.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> +#include <linux/workqueue.h> +#include <net/checksum.h> +#include <net/ip6_checksum.h> + +#include "atl1c_hw.h" + +/* Wake Up Filter Control */ +#define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ +#define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ + +#define AT_VLAN_TO_TAG(_vlan, _tag) \ + _tag = ((((_vlan) >> 8) & 0xFF) |\ + (((_vlan) & 0xFF) << 8)) + +#define AT_TAG_TO_VLAN(_tag, _vlan) \ + _vlan = ((((_tag) >> 8) & 0xFF) |\ + (((_tag) & 0xFF) << 8)) + +#define SPEED_0 0xffff +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +#define AT_RX_BUF_SIZE (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN) +#define MAX_JUMBO_FRAME_SIZE (9*1024) +#define MAX_TX_OFFLOAD_THRESH (9*1024) + +#define AT_MAX_RECEIVE_QUEUE 4 +#define AT_DEF_RECEIVE_QUEUE 1 +#define AT_MAX_TRANSMIT_QUEUE 2 + +#define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL +#define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL + +#define AT_TX_WATCHDOG (5 * HZ) +#define AT_MAX_INT_WORK 5 +#define AT_TWSI_EEPROM_TIMEOUT 100 +#define AT_HW_MAX_IDLE_DELAY 10 +#define AT_SUSPEND_LINK_TIMEOUT 28 + +#define AT_ASPM_L0S_TIMER 6 +#define AT_ASPM_L1_TIMER 12 + +#define ATL1C_PCIE_L0S_L1_DISABLE 0x01 +#define ATL1C_PCIE_PHY_RESET 0x02 + +#define ATL1C_ASPM_L0s_ENABLE 0x0001 +#define ATL1C_ASPM_L1_ENABLE 0x0002 + +#define AT_REGS_LEN (75 * sizeof(u32)) +#define AT_EEPROM_LEN 512 + +#define ATL1C_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i])) +#define ATL1C_RFD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_rx_free_desc) +#define ATL1C_TPD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_tpd_desc) +#define ATL1C_RRD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_recv_ret_status) + +/* tpd word 1 bit 0:7 General Checksum task offload */ +#define TPD_L4HDR_OFFSET_MASK 0x00FF +#define TPD_L4HDR_OFFSET_SHIFT 0 + +/* tpd word 1 bit 0:7 Large Send task offload (IPv4/IPV6) */ +#define TPD_TCPHDR_OFFSET_MASK 0x00FF +#define TPD_TCPHDR_OFFSET_SHIFT 0 + +/* tpd word 1 bit 0:7 Custom Checksum task offload */ +#define TPD_PLOADOFFSET_MASK 0x00FF +#define TPD_PLOADOFFSET_SHIFT 0 + +/* tpd word 1 bit 8:17 */ +#define TPD_CCSUM_EN_MASK 0x0001 +#define TPD_CCSUM_EN_SHIFT 8 +#define TPD_IP_CSUM_MASK 0x0001 +#define TPD_IP_CSUM_SHIFT 9 +#define TPD_TCP_CSUM_MASK 0x0001 +#define TPD_TCP_CSUM_SHIFT 10 +#define TPD_UDP_CSUM_MASK 0x0001 +#define TPD_UDP_CSUM_SHIFT 11 +#define TPD_LSO_EN_MASK 0x0001 /* TCP Large Send Offload */ +#define TPD_LSO_EN_SHIFT 12 +#define TPD_LSO_VER_MASK 0x0001 +#define TPD_LSO_VER_SHIFT 13 /* 0 : ipv4; 1 : ipv4/ipv6 */ +#define TPD_CON_VTAG_MASK 0x0001 +#define TPD_CON_VTAG_SHIFT 14 +#define TPD_INS_VTAG_MASK 0x0001 +#define TPD_INS_VTAG_SHIFT 15 +#define TPD_IPV4_PACKET_MASK 0x0001 /* valid when LSO VER is 1 */ +#define TPD_IPV4_PACKET_SHIFT 16 +#define TPD_ETH_TYPE_MASK 0x0001 +#define TPD_ETH_TYPE_SHIFT 17 /* 0 : 802.3 frame; 1 : Ethernet */ + +/* tpd word 18:25 Custom Checksum task offload */ +#define TPD_CCSUM_OFFSET_MASK 0x00FF +#define TPD_CCSUM_OFFSET_SHIFT 18 +#define TPD_CCSUM_EPAD_MASK 0x0001 +#define TPD_CCSUM_EPAD_SHIFT 30 + +/* tpd word 18:30 Large Send task offload (IPv4/IPV6) */ +#define TPD_MSS_MASK 0x1FFF +#define TPD_MSS_SHIFT 18 + +#define TPD_EOP_MASK 0x0001 +#define TPD_EOP_SHIFT 31 + +struct atl1c_tpd_desc { + __le16 buffer_len; /* include 4-byte CRC */ + __le16 vlan_tag; + __le32 word1; + __le64 buffer_addr; +}; + +struct atl1c_tpd_ext_desc { + u32 reservd_0; + __le32 word1; + __le32 pkt_len; + u32 reservd_1; +}; +/* rrs word 0 bit 0:31 */ +#define RRS_RX_CSUM_MASK 0xFFFF +#define RRS_RX_CSUM_SHIFT 0 +#define RRS_RX_RFD_CNT_MASK 0x000F +#define RRS_RX_RFD_CNT_SHIFT 16 +#define RRS_RX_RFD_INDEX_MASK 0x0FFF +#define RRS_RX_RFD_INDEX_SHIFT 20 + +/* rrs flag bit 0:16 */ +#define RRS_HEAD_LEN_MASK 0x00FF +#define RRS_HEAD_LEN_SHIFT 0 +#define RRS_HDS_TYPE_MASK 0x0003 +#define RRS_HDS_TYPE_SHIFT 8 +#define RRS_CPU_NUM_MASK 0x0003 +#define RRS_CPU_NUM_SHIFT 10 +#define RRS_HASH_FLG_MASK 0x000F +#define RRS_HASH_FLG_SHIFT 12 + +#define RRS_HDS_TYPE_HEAD 1 +#define RRS_HDS_TYPE_DATA 2 + +#define RRS_IS_NO_HDS_TYPE(flag) \ + (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == 0) + +#define RRS_IS_HDS_HEAD(flag) \ + (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \ + RRS_HDS_TYPE_HEAD) + +#define RRS_IS_HDS_DATA(flag) \ + (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \ + RRS_HDS_TYPE_DATA) + +/* rrs word 3 bit 0:31 */ +#define RRS_PKT_SIZE_MASK 0x3FFF +#define RRS_PKT_SIZE_SHIFT 0 +#define RRS_ERR_L4_CSUM_MASK 0x0001 +#define RRS_ERR_L4_CSUM_SHIFT 14 +#define RRS_ERR_IP_CSUM_MASK 0x0001 +#define RRS_ERR_IP_CSUM_SHIFT 15 +#define RRS_VLAN_INS_MASK 0x0001 +#define RRS_VLAN_INS_SHIFT 16 +#define RRS_PROT_ID_MASK 0x0007 +#define RRS_PROT_ID_SHIFT 17 +#define RRS_RX_ERR_SUM_MASK 0x0001 +#define RRS_RX_ERR_SUM_SHIFT 20 +#define RRS_RX_ERR_CRC_MASK 0x0001 +#define RRS_RX_ERR_CRC_SHIFT 21 +#define RRS_RX_ERR_FAE_MASK 0x0001 +#define RRS_RX_ERR_FAE_SHIFT 22 +#define RRS_RX_ERR_TRUNC_MASK 0x0001 +#define RRS_RX_ERR_TRUNC_SHIFT 23 +#define RRS_RX_ERR_RUNC_MASK 0x0001 +#define RRS_RX_ERR_RUNC_SHIFT 24 +#define RRS_RX_ERR_ICMP_MASK 0x0001 +#define RRS_RX_ERR_ICMP_SHIFT 25 +#define RRS_PACKET_BCAST_MASK 0x0001 +#define RRS_PACKET_BCAST_SHIFT 26 +#define RRS_PACKET_MCAST_MASK 0x0001 +#define RRS_PACKET_MCAST_SHIFT 27 +#define RRS_PACKET_TYPE_MASK 0x0001 +#define RRS_PACKET_TYPE_SHIFT 28 +#define RRS_FIFO_FULL_MASK 0x0001 +#define RRS_FIFO_FULL_SHIFT 29 +#define RRS_802_3_LEN_ERR_MASK 0x0001 +#define RRS_802_3_LEN_ERR_SHIFT 30 +#define RRS_RXD_UPDATED_MASK 0x0001 +#define RRS_RXD_UPDATED_SHIFT 31 + +#define RRS_ERR_L4_CSUM 0x00004000 +#define RRS_ERR_IP_CSUM 0x00008000 +#define RRS_VLAN_INS 0x00010000 +#define RRS_RX_ERR_SUM 0x00100000 +#define RRS_RX_ERR_CRC 0x00200000 +#define RRS_802_3_LEN_ERR 0x40000000 +#define RRS_RXD_UPDATED 0x80000000 + +#define RRS_PACKET_TYPE_802_3 1 +#define RRS_PACKET_TYPE_ETH 0 +#define RRS_PACKET_IS_ETH(word) \ + (((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK == \ + RRS_PACKET_TYPE_ETH) +#define RRS_RXD_IS_VALID(word) \ + ((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1) + +#define RRS_PACKET_PROT_IS_IPV4_ONLY(word) \ + ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 1) +#define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \ + ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6) + +struct atl1c_recv_ret_status { + __le32 word0; + __le32 rss_hash; + __le16 vlan_tag; + __le16 flag; + __le32 word3; +}; + +/* RFD desciptor */ +struct atl1c_rx_free_desc { + __le64 buffer_addr; +}; + +/* DMA Order Settings */ +enum atl1c_dma_order { + atl1c_dma_ord_in = 1, + atl1c_dma_ord_enh = 2, + atl1c_dma_ord_out = 4 +}; + +enum atl1c_dma_rcb { + atl1c_rcb_64 = 0, + atl1c_rcb_128 = 1 +}; + +enum atl1c_mac_speed { + atl1c_mac_speed_0 = 0, + atl1c_mac_speed_10_100 = 1, + atl1c_mac_speed_1000 = 2 +}; + +enum atl1c_dma_req_block { + atl1c_dma_req_128 = 0, + atl1c_dma_req_256 = 1, + atl1c_dma_req_512 = 2, + atl1c_dma_req_1024 = 3, + atl1c_dma_req_2048 = 4, + atl1c_dma_req_4096 = 5 +}; + +enum atl1c_rss_mode { + atl1c_rss_mode_disable = 0, + atl1c_rss_sig_que = 1, + atl1c_rss_mul_que_sig_int = 2, + atl1c_rss_mul_que_mul_int = 4, +}; + +enum atl1c_rss_type { + atl1c_rss_disable = 0, + atl1c_rss_ipv4 = 1, + atl1c_rss_ipv4_tcp = 2, + atl1c_rss_ipv6 = 4, + atl1c_rss_ipv6_tcp = 8 +}; + +enum atl1c_nic_type { + athr_l1c = 0, + athr_l2c = 1, +}; + +enum atl1c_trans_queue { + atl1c_trans_normal = 0, + atl1c_trans_high = 1 +}; + +struct atl1c_hw_stats { + /* rx */ + unsigned long rx_ok; /* The number of good packet received. */ + unsigned long rx_bcast; /* The number of good broadcast packet received. */ + unsigned long rx_mcast; /* The number of good multicast packet received. */ + unsigned long rx_pause; /* The number of Pause packet received. */ + unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */ + unsigned long rx_fcs_err; /* The number of packets with bad FCS. */ + unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */ + unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */ + unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */ + unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */ + unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */ + unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */ + unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */ + unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */ + unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */ + unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */ + unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */ + unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */ + unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */ + unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */ + unsigned long rx_align_err; /* Alignment Error */ + unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */ + unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */ + unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */ + + /* tx */ + unsigned long tx_ok; /* The number of good packet transmitted. */ + unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */ + unsigned long tx_mcast; /* The number of good multicast packet transmitted. */ + unsigned long tx_pause; /* The number of Pause packet transmitted. */ + unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */ + unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */ + unsigned long tx_defer; /* The number of packets transmitted that is deferred. */ + unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */ + unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */ + unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */ + unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */ + unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */ + unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */ + unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */ + unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */ + unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */ + unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */ + unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */ + unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */ + unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */ + unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */ + unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */ + unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */ + unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */ + unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */ +}; + +struct atl1c_hw { + u8 __iomem *hw_addr; /* inner register address */ + struct atl1c_adapter *adapter; + enum atl1c_nic_type nic_type; + enum atl1c_dma_order dma_order; + enum atl1c_dma_rcb rcb_value; + enum atl1c_dma_req_block dmar_block; + enum atl1c_dma_req_block dmaw_block; + + u16 device_id; + u16 vendor_id; + u16 subsystem_id; + u16 subsystem_vendor_id; + u8 revision_id; + + u32 intr_mask; + u8 dmaw_dly_cnt; + u8 dmar_dly_cnt; + + u8 preamble_len; + u16 max_frame_size; + u16 min_frame_size; + + enum atl1c_mac_speed mac_speed; + bool mac_duplex; + bool hibernate; + u16 media_type; +#define MEDIA_TYPE_AUTO_SENSOR 0 +#define MEDIA_TYPE_100M_FULL 1 +#define MEDIA_TYPE_100M_HALF 2 +#define MEDIA_TYPE_10M_FULL 3 +#define MEDIA_TYPE_10M_HALF 4 + + u16 autoneg_advertised; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg; + + u16 tx_imt; /* TX Interrupt Moderator timer ( 2us resolution) */ + u16 rx_imt; /* RX Interrupt Moderator timer ( 2us resolution) */ + u16 ict; /* Interrupt Clear timer (2us resolution) */ + u16 ctrl_flags; +#define ATL1C_INTR_CLEAR_ON_READ 0x0001 +#define ATL1C_INTR_MODRT_ENABLE 0x0002 +#define ATL1C_CMB_ENABLE 0x0004 +#define ATL1C_SMB_ENABLE 0x0010 +#define ATL1C_TXQ_MODE_ENHANCE 0x0020 +#define ATL1C_RX_IPV6_CHKSUM 0x0040 +#define ATL1C_ASPM_L0S_SUPPORT 0x0080 +#define ATL1C_ASPM_L1_SUPPORT 0x0100 +#define ATL1C_ASPM_CTRL_MON 0x0200 +#define ATL1C_HIB_DISABLE 0x0400 +#define ATL1C_LINK_CAP_1000M 0x0800 +#define ATL1C_FPGA_VERSION 0x8000 + u16 cmb_tpd; + u16 cmb_rrd; + u16 cmb_rx_timer; /* 2us resolution */ + u16 cmb_tx_timer; + u32 smb_timer; + + u16 rrd_thresh; /* Threshold of number of RRD produced to trigger + interrupt request */ + u16 tpd_thresh; + u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */ + u8 rfd_burst; + enum atl1c_rss_type rss_type; + enum atl1c_rss_mode rss_mode; + u8 rss_hash_bits; + u32 base_cpu; + u32 indirect_tab; + u8 mac_addr[ETH_ALEN]; + u8 perm_mac_addr[ETH_ALEN]; + + bool phy_configured; + bool re_autoneg; + bool emi_ca; +}; + +/* + * atl1c_ring_header represents a single, contiguous block of DMA space + * mapped for the three descriptor rings (tpd, rfd, rrd) and the two + * message blocks (cmb, smb) described below + */ +struct atl1c_ring_header { + void *desc; /* virtual address */ + dma_addr_t dma; /* physical address*/ + unsigned int size; /* length in bytes */ +}; + +/* + * atl1c_buffer is wrapper around a pointer to a socket buffer + * so a DMA handle can be stored along with the skb + */ +struct atl1c_buffer { + struct sk_buff *skb; /* socket buffer */ + u16 length; /* rx buffer length */ + u16 state; /* state of buffer */ +#define ATL1_BUFFER_FREE 0 +#define ATL1_BUFFER_BUSY 1 + dma_addr_t dma; +}; + +/* transimit packet descriptor (tpd) ring */ +struct atl1c_tpd_ring { + void *desc; /* descriptor ring virtual address */ + dma_addr_t dma; /* descriptor ring physical address */ + u16 size; /* descriptor ring length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 next_to_use; /* this is protectd by adapter->tx_lock */ + atomic_t next_to_clean; + struct atl1c_buffer *buffer_info; +}; + +/* receive free descriptor (rfd) ring */ +struct atl1c_rfd_ring { + void *desc; /* descriptor ring virtual address */ + dma_addr_t dma; /* descriptor ring physical address */ + u16 size; /* descriptor ring length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 next_to_use; + u16 next_to_clean; + struct atl1c_buffer *buffer_info; +}; + +/* receive return desciptor (rrd) ring */ +struct atl1c_rrd_ring { + void *desc; /* descriptor ring virtual address */ + dma_addr_t dma; /* descriptor ring physical address */ + u16 size; /* descriptor ring length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 next_to_use; + u16 next_to_clean; +}; + +struct atl1c_cmb { + void *cmb; + dma_addr_t dma; +}; + +struct atl1c_smb { + void *smb; + dma_addr_t dma; +}; + +/* board specific private data structure */ +struct atl1c_adapter { + struct net_device *netdev; + struct pci_dev *pdev; + struct vlan_group *vlgrp; + struct napi_struct napi; + struct atl1c_hw hw; + struct atl1c_hw_stats hw_stats; + struct net_device_stats net_stats; + struct mii_if_info mii; /* MII interface info */ + u16 rx_buffer_len; + + unsigned long flags; +#define __AT_TESTING 0x0001 +#define __AT_RESETTING 0x0002 +#define __AT_DOWN 0x0003 + u32 msg_enable; + + bool have_msi; + u32 wol; + u16 link_speed; + u16 link_duplex; + + spinlock_t mdio_lock; + spinlock_t tx_lock; + atomic_t irq_sem; + + struct work_struct reset_task; + struct work_struct link_chg_task; + struct timer_list watchdog_timer; + struct timer_list phy_config_timer; + + /* All Descriptor memory */ + struct atl1c_ring_header ring_header; + struct atl1c_tpd_ring tpd_ring[AT_MAX_TRANSMIT_QUEUE]; + struct atl1c_rfd_ring rfd_ring[AT_MAX_RECEIVE_QUEUE]; + struct atl1c_rrd_ring rrd_ring[AT_MAX_RECEIVE_QUEUE]; + struct atl1c_cmb cmb; + struct atl1c_smb smb; + int num_rx_queues; + u32 bd_number; /* board number;*/ +}; + +#define AT_WRITE_REG(a, reg, value) ( \ + writel((value), ((a)->hw_addr + reg))) + +#define AT_WRITE_FLUSH(a) (\ + readl((a)->hw_addr)) + +#define AT_READ_REG(a, reg, pdata) do { \ + if (unlikely((a)->hibernate)) { \ + readl((a)->hw_addr + reg); \ + *(u32 *)pdata = readl((a)->hw_addr + reg); \ + } else { \ + *(u32 *)pdata = readl((a)->hw_addr + reg); \ + } \ + } while (0) + +#define AT_WRITE_REGB(a, reg, value) (\ + writeb((value), ((a)->hw_addr + reg))) + +#define AT_READ_REGB(a, reg) (\ + readb((a)->hw_addr + reg)) + +#define AT_WRITE_REGW(a, reg, value) (\ + writew((value), ((a)->hw_addr + reg))) + +#define AT_READ_REGW(a, reg) (\ + readw((a)->hw_addr + reg)) + +#define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \ + writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))) + +#define AT_READ_REG_ARRAY(a, reg, offset) ( \ + readl(((a)->hw_addr + reg) + ((offset) << 2))) + +extern char atl1c_driver_name[]; +extern char atl1c_driver_version[]; + +extern int atl1c_up(struct atl1c_adapter *adapter); +extern void atl1c_down(struct atl1c_adapter *adapter); +extern void atl1c_reinit_locked(struct atl1c_adapter *adapter); +extern s32 atl1c_reset_hw(struct atl1c_hw *hw); +extern void atl1c_set_ethtool_ops(struct net_device *netdev); +#endif /* _ATL1C_H_ */ diff --git a/drivers/net/atl1c/atl1c_ethtool.c b/drivers/net/atl1c/atl1c_ethtool.c new file mode 100644 index 000000000000..45c5b7332cd3 --- /dev/null +++ b/drivers/net/atl1c/atl1c_ethtool.c @@ -0,0 +1,317 @@ +/* + * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/netdevice.h> +#include <linux/ethtool.h> + +#include "atl1c.h" + +static int atl1c_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP); + if (hw->ctrl_flags & ATL1C_LINK_CAP_1000M) + ecmd->supported |= SUPPORTED_1000baseT_Full; + + ecmd->advertising = ADVERTISED_TP; + + ecmd->advertising |= hw->autoneg_advertised; + + ecmd->port = PORT_TP; + ecmd->phy_address = 0; + ecmd->transceiver = XCVR_INTERNAL; + + if (adapter->link_speed != SPEED_0) { + ecmd->speed = adapter->link_speed; + if (adapter->link_duplex == FULL_DUPLEX) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + + ecmd->autoneg = AUTONEG_ENABLE; + return 0; +} + +static int atl1c_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u16 autoneg_advertised; + + while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) + msleep(1); + + if (ecmd->autoneg == AUTONEG_ENABLE) { + autoneg_advertised = ADVERTISED_Autoneg; + } else { + if (ecmd->speed == SPEED_1000) { + if (ecmd->duplex != DUPLEX_FULL) { + if (netif_msg_link(adapter)) + dev_warn(&adapter->pdev->dev, + "1000M half is invalid\n"); + clear_bit(__AT_RESETTING, &adapter->flags); + return -EINVAL; + } + autoneg_advertised = ADVERTISED_1000baseT_Full; + } else if (ecmd->speed == SPEED_100) { + if (ecmd->duplex == DUPLEX_FULL) + autoneg_advertised = ADVERTISED_100baseT_Full; + else + autoneg_advertised = ADVERTISED_100baseT_Half; + } else { + if (ecmd->duplex == DUPLEX_FULL) + autoneg_advertised = ADVERTISED_10baseT_Full; + else + autoneg_advertised = ADVERTISED_10baseT_Half; + } + } + + if (hw->autoneg_advertised != autoneg_advertised) { + hw->autoneg_advertised = autoneg_advertised; + if (atl1c_restart_autoneg(hw) != 0) { + if (netif_msg_link(adapter)) + dev_warn(&adapter->pdev->dev, + "ethtool speed/duplex setting failed\n"); + clear_bit(__AT_RESETTING, &adapter->flags); + return -EINVAL; + } + } + clear_bit(__AT_RESETTING, &adapter->flags); + return 0; +} + +static u32 atl1c_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} + +static u32 atl1c_get_msglevel(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + return adapter->msg_enable; +} + +static void atl1c_set_msglevel(struct net_device *netdev, u32 data) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + adapter->msg_enable = data; +} + +static int atl1c_get_regs_len(struct net_device *netdev) +{ + return AT_REGS_LEN; +} + +static void atl1c_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u32 *regs_buff = p; + u16 phy_data; + + memset(p, 0, AT_REGS_LEN); + + regs->version = 0; + AT_READ_REG(hw, REG_VPD_CAP, p++); + AT_READ_REG(hw, REG_PM_CTRL, p++); + AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++); + AT_READ_REG(hw, REG_TWSI_CTRL, p++); + AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++); + AT_READ_REG(hw, REG_MASTER_CTRL, p++); + AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++); + AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++); + AT_READ_REG(hw, REG_GPHY_CTRL, p++); + AT_READ_REG(hw, REG_LINK_CTRL, p++); + AT_READ_REG(hw, REG_IDLE_STATUS, p++); + AT_READ_REG(hw, REG_MDIO_CTRL, p++); + AT_READ_REG(hw, REG_SERDES_LOCK, p++); + AT_READ_REG(hw, REG_MAC_CTRL, p++); + AT_READ_REG(hw, REG_MAC_IPG_IFG, p++); + AT_READ_REG(hw, REG_MAC_STA_ADDR, p++); + AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++); + AT_READ_REG(hw, REG_RX_HASH_TABLE, p++); + AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++); + AT_READ_REG(hw, REG_RXQ_CTRL, p++); + AT_READ_REG(hw, REG_TXQ_CTRL, p++); + AT_READ_REG(hw, REG_MTU, p++); + AT_READ_REG(hw, REG_WOL_CTRL, p++); + + atl1c_read_phy_reg(hw, MII_BMCR, &phy_data); + regs_buff[73] = (u32) phy_data; + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + regs_buff[74] = (u32) phy_data; +} + +static int atl1c_get_eeprom_len(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (atl1c_check_eeprom_exist(&adapter->hw)) + return AT_EEPROM_LEN; + else + return 0; +} + +static int atl1c_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u32 *eeprom_buff; + int first_dword, last_dword; + int ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EINVAL; + + if (!atl1c_check_eeprom_exist(hw)) /* not exist */ + return -EINVAL; + + eeprom->magic = adapter->pdev->vendor | + (adapter->pdev->device << 16); + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + + eeprom_buff = kmalloc(sizeof(u32) * + (last_dword - first_dword + 1), GFP_KERNEL); + if (eeprom_buff == NULL) + return -ENOMEM; + + for (i = first_dword; i < last_dword; i++) { + if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { + kfree(eeprom_buff); + return -EIO; + } + } + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), + eeprom->len); + kfree(eeprom_buff); + + return ret_val; + return 0; +} + +static void atl1c_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + strncpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); + strncpy(drvinfo->version, atl1c_driver_version, + sizeof(drvinfo->version)); + strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), + sizeof(drvinfo->bus_info)); + drvinfo->n_stats = 0; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = atl1c_get_regs_len(netdev); + drvinfo->eedump_len = atl1c_get_eeprom_len(netdev); +} + +static void atl1c_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_MAGIC | WAKE_PHY; + wol->wolopts = 0; + + if (adapter->wol & AT_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if (adapter->wol & AT_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if (adapter->wol & AT_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if (adapter->wol & AT_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + if (adapter->wol & AT_WUFC_LNKC) + wol->wolopts |= WAKE_PHY; + + return; +} + +static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | + WAKE_MCAST | WAKE_BCAST | WAKE_MCAST)) + return -EOPNOTSUPP; + /* these settings will always override what we currently have */ + adapter->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + adapter->wol |= AT_WUFC_MAG; + if (wol->wolopts & WAKE_PHY) + adapter->wol |= AT_WUFC_LNKC; + + return 0; +} + +static int atl1c_nway_reset(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + if (netif_running(netdev)) + atl1c_reinit_locked(adapter); + return 0; +} + +static struct ethtool_ops atl1c_ethtool_ops = { + .get_settings = atl1c_get_settings, + .set_settings = atl1c_set_settings, + .get_drvinfo = atl1c_get_drvinfo, + .get_regs_len = atl1c_get_regs_len, + .get_regs = atl1c_get_regs, + .get_wol = atl1c_get_wol, + .set_wol = atl1c_set_wol, + .get_msglevel = atl1c_get_msglevel, + .set_msglevel = atl1c_set_msglevel, + .nway_reset = atl1c_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = atl1c_get_eeprom_len, + .get_eeprom = atl1c_get_eeprom, + .get_tx_csum = atl1c_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +}; + +void atl1c_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops); +} diff --git a/drivers/net/atl1c/atl1c_hw.c b/drivers/net/atl1c/atl1c_hw.c new file mode 100644 index 000000000000..3e69b940b8f7 --- /dev/null +++ b/drivers/net/atl1c/atl1c_hw.c @@ -0,0 +1,527 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include <linux/crc32.h> + +#include "atl1c.h" + +/* + * check_eeprom_exist + * return 1 if eeprom exist + */ +int atl1c_check_eeprom_exist(struct atl1c_hw *hw) +{ + u32 data; + + AT_READ_REG(hw, REG_TWSI_DEBUG, &data); + if (data & TWSI_DEBUG_DEV_EXIST) + return 1; + + return 0; +} + +void atl1c_hw_set_mac_addr(struct atl1c_hw *hw) +{ + u32 value; + /* + * 00-0B-6A-F6-00-DC + * 0: 6AF600DC 1: 000B + * low dword + */ + value = (((u32)hw->mac_addr[2]) << 24) | + (((u32)hw->mac_addr[3]) << 16) | + (((u32)hw->mac_addr[4]) << 8) | + (((u32)hw->mac_addr[5])) ; + AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); + /* hight dword */ + value = (((u32)hw->mac_addr[0]) << 8) | + (((u32)hw->mac_addr[1])) ; + AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); +} + +/* + * atl1c_get_permanent_address + * return 0 if get valid mac address, + */ +static int atl1c_get_permanent_address(struct atl1c_hw *hw) +{ + u32 addr[2]; + u32 i; + u32 otp_ctrl_data; + u32 twsi_ctrl_data; + u8 eth_addr[ETH_ALEN]; + + /* init */ + addr[0] = addr[1] = 0; + AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); + if (atl1c_check_eeprom_exist(hw)) { + /* Enable OTP CLK */ + if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) { + otp_ctrl_data |= OTP_CTRL_CLK_EN; + AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(1); + } + + AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); + twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; + AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data); + for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) { + msleep(10); + AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); + if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0) + break; + } + if (i >= AT_TWSI_EEPROM_TIMEOUT) + return -1; + } + /* Disable OTP_CLK */ + if (otp_ctrl_data & OTP_CTRL_CLK_EN) { + otp_ctrl_data &= ~OTP_CTRL_CLK_EN; + AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(1); + } + + /* maybe MAC-address is from BIOS */ + AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]); + AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]); + *(u32 *) ð_addr[2] = swab32(addr[0]); + *(u16 *) ð_addr[0] = swab16(*(u16 *)&addr[1]); + + if (is_valid_ether_addr(eth_addr)) { + memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); + return 0; + } + + return -1; +} + +bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value) +{ + int i; + int ret = false; + u32 otp_ctrl_data; + u32 control; + u32 data; + + if (offset & 3) + return ret; /* address do not align */ + + AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); + if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) + AT_WRITE_REG(hw, REG_OTP_CTRL, + (otp_ctrl_data | OTP_CTRL_CLK_EN)); + + AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0); + control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT; + AT_WRITE_REG(hw, REG_EEPROM_CTRL, control); + + for (i = 0; i < 10; i++) { + udelay(100); + AT_READ_REG(hw, REG_EEPROM_CTRL, &control); + if (control & EEPROM_CTRL_RW) + break; + } + if (control & EEPROM_CTRL_RW) { + AT_READ_REG(hw, REG_EEPROM_CTRL, &data); + AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value); + data = data & 0xFFFF; + *p_value = swab32((data << 16) | (*p_value >> 16)); + ret = true; + } + if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) + AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); + + return ret; +} +/* + * Reads the adapter's MAC address from the EEPROM + * + * hw - Struct containing variables accessed by shared code + */ +int atl1c_read_mac_addr(struct atl1c_hw *hw) +{ + int err = 0; + + err = atl1c_get_permanent_address(hw); + if (err) + random_ether_addr(hw->perm_mac_addr); + + memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr)); + return 0; +} + +/* + * atl1c_hash_mc_addr + * purpose + * set hash value for a multicast address + * hash calcu processing : + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + */ +u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr) +{ + u32 crc32; + u32 value = 0; + int i; + + crc32 = ether_crc_le(6, mc_addr); + for (i = 0; i < 32; i++) + value |= (((crc32 >> i) & 1) << (31 - i)); + + return value; +} + +/* + * Sets the bit in the multicast table corresponding to the hash value. + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + */ +void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg; + u32 mta; + + /* + * The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper bit of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 31) & 0x1; + hash_bit = (hash_value >> 26) & 0x1F; + + mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); + + mta |= (1 << hash_bit); + + AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); +} + +/* + * Reads the value from a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to read + */ +int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) +{ + u32 val; + int i; + + val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | + MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + + AT_WRITE_REG(hw, REG_MDIO_CTRL, val); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + AT_READ_REG(hw, REG_MDIO_CTRL, &val); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + if (!(val & (MDIO_START | MDIO_BUSY))) { + *phy_data = (u16)val; + return 0; + } + + return -1; +} + +/* + * Writes a value to a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to write + * data - data to write to the PHY + */ +int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data) +{ + int i; + u32 val; + + val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | + (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | + MDIO_SUP_PREAMBLE | MDIO_START | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + + AT_WRITE_REG(hw, REG_MDIO_CTRL, val); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + AT_READ_REG(hw, REG_MDIO_CTRL, &val); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + + if (!(val & (MDIO_START | MDIO_BUSY))) + return 0; + + return -1; +} + +/* + * Configures PHY autoneg and flow control advertisement settings + * + * hw - Struct containing variables accessed by shared code + */ +static int atl1c_phy_setup_adv(struct atl1c_hw *hw) +{ + u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_SPEED_MASK; + u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP & + ~GIGA_CR_1000T_SPEED_MASK; + + if (hw->autoneg_advertised & ADVERTISED_10baseT_Half) + mii_adv_data |= ADVERTISE_10HALF; + if (hw->autoneg_advertised & ADVERTISED_10baseT_Full) + mii_adv_data |= ADVERTISE_10FULL; + if (hw->autoneg_advertised & ADVERTISED_100baseT_Half) + mii_adv_data |= ADVERTISE_100HALF; + if (hw->autoneg_advertised & ADVERTISED_100baseT_Full) + mii_adv_data |= ADVERTISE_100FULL; + + if (hw->autoneg_advertised & ADVERTISED_Autoneg) + mii_adv_data |= ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL; + + if (hw->ctrl_flags & ATL1C_LINK_CAP_1000M) { + if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half) + mii_giga_ctrl_data |= ADVERTISE_1000HALF; + if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full) + mii_giga_ctrl_data |= ADVERTISE_1000FULL; + if (hw->autoneg_advertised & ADVERTISED_Autoneg) + mii_giga_ctrl_data |= ADVERTISE_1000HALF | + ADVERTISE_1000FULL; + } + + if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 || + atl1c_write_phy_reg(hw, MII_GIGA_CR, mii_giga_ctrl_data) != 0) + return -1; + return 0; +} + +void atl1c_phy_disable(struct atl1c_hw *hw) +{ + AT_WRITE_REGW(hw, REG_GPHY_CTRL, + GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET); +} + +static void atl1c_phy_magic_data(struct atl1c_hw *hw) +{ + u16 data; + + data = ANA_LOOP_SEL_10BT | ANA_EN_MASK_TB | ANA_EN_10BT_IDLE | + ((1 & ANA_INTERVAL_SEL_TIMER_MASK) << + ANA_INTERVAL_SEL_TIMER_SHIFT); + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_18); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = (2 & ANA_SERDES_CDR_BW_MASK) | ANA_MS_PAD_DBG | + ANA_SERDES_EN_DEEM | ANA_SERDES_SEL_HSP | ANA_SERDES_EN_PLL | + ANA_SERDES_EN_LCKDT; + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_5); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = (44 & ANA_LONG_CABLE_TH_100_MASK) | + ((33 & ANA_SHORT_CABLE_TH_100_MASK) << + ANA_SHORT_CABLE_TH_100_SHIFT) | ANA_BP_BAD_LINK_ACCUM | + ANA_BP_SMALL_BW; + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_54); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = (11 & ANA_IECHO_ADJ_MASK) | ((11 & ANA_IECHO_ADJ_MASK) << + ANA_IECHO_ADJ_2_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) << + ANA_IECHO_ADJ_1_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) << + ANA_IECHO_ADJ_0_SHIFT); + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_4); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = ANA_RESTART_CAL | ((7 & ANA_MANUL_SWICH_ON_MASK) << + ANA_MANUL_SWICH_ON_SHIFT) | ANA_MAN_ENABLE | + ANA_SEL_HSP | ANA_EN_HB | ANA_OEN_125M; + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_0); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + if (hw->ctrl_flags & ATL1C_HIB_DISABLE) { + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_41); + if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0) + return; + data &= ~ANA_TOP_PS_EN; + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_11); + if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0) + return; + data &= ~ANA_PS_HIB_EN; + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + } +} + +int atl1c_phy_reset(struct atl1c_hw *hw) +{ + struct atl1c_adapter *adapter = hw->adapter; + struct pci_dev *pdev = adapter->pdev; + u32 phy_ctrl_data = GPHY_CTRL_DEFAULT; + u32 mii_ier_data = IER_LINK_UP | IER_LINK_DOWN; + int err; + + if (hw->ctrl_flags & ATL1C_HIB_DISABLE) + phy_ctrl_data &= ~GPHY_CTRL_HIB_EN; + + AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(40); + phy_ctrl_data |= GPHY_CTRL_EXT_RESET; + AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(10); + + /*Enable PHY LinkChange Interrupt */ + err = atl1c_write_phy_reg(hw, MII_IER, mii_ier_data); + if (err) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "Error enable PHY linkChange Interrupt\n"); + return err; + } + if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) + atl1c_phy_magic_data(hw); + return 0; +} + +int atl1c_phy_init(struct atl1c_hw *hw) +{ + struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct pci_dev *pdev = adapter->pdev; + int ret_val; + u16 mii_bmcr_data = BMCR_RESET; + u16 phy_id1, phy_id2; + + if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &phy_id1) != 0) || + (atl1c_read_phy_reg(hw, MII_PHYSID2, &phy_id2) != 0)) { + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, "Error get phy ID\n"); + return -1; + } + switch (hw->media_type) { + case MEDIA_TYPE_AUTO_SENSOR: + ret_val = atl1c_phy_setup_adv(hw); + if (ret_val) { + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, + "Error Setting up Auto-Negotiation\n"); + return ret_val; + } + mii_bmcr_data |= BMCR_AUTO_NEG_EN | BMCR_RESTART_AUTO_NEG; + break; + case MEDIA_TYPE_100M_FULL: + mii_bmcr_data |= BMCR_SPEED_100 | BMCR_FULL_DUPLEX; + break; + case MEDIA_TYPE_100M_HALF: + mii_bmcr_data |= BMCR_SPEED_100; + break; + case MEDIA_TYPE_10M_FULL: + mii_bmcr_data |= BMCR_SPEED_10 | BMCR_FULL_DUPLEX; + break; + case MEDIA_TYPE_10M_HALF: + mii_bmcr_data |= BMCR_SPEED_10; + break; + default: + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, "Wrong Media type %d\n", + hw->media_type); + return -1; + break; + } + + ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); + if (ret_val) + return ret_val; + hw->phy_configured = true; + + return 0; +} + +/* + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + */ +int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex) +{ + int err; + u16 phy_data; + + /* Read PHY Specific Status Register (17) */ + err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data); + if (err) + return err; + + if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED)) + return -1; + + switch (phy_data & GIGA_PSSR_SPEED) { + case GIGA_PSSR_1000MBS: + *speed = SPEED_1000; + break; + case GIGA_PSSR_100MBS: + *speed = SPEED_100; + break; + case GIGA_PSSR_10MBS: + *speed = SPEED_10; + break; + default: + return -1; + break; + } + + if (phy_data & GIGA_PSSR_DPLX) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + + return 0; +} + +int atl1c_restart_autoneg(struct atl1c_hw *hw) +{ + int err = 0; + u16 mii_bmcr_data = BMCR_RESET; + + err = atl1c_phy_setup_adv(hw); + if (err) + return err; + mii_bmcr_data |= BMCR_AUTO_NEG_EN | BMCR_RESTART_AUTO_NEG; + + return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); +} diff --git a/drivers/net/atl1c/atl1c_hw.h b/drivers/net/atl1c/atl1c_hw.h new file mode 100644 index 000000000000..c2c738df5c63 --- /dev/null +++ b/drivers/net/atl1c/atl1c_hw.h @@ -0,0 +1,859 @@ +/* + * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ATL1C_HW_H_ +#define _ATL1C_HW_H_ + +#include <linux/types.h> +#include <linux/mii.h> + +struct atl1c_adapter; +struct atl1c_hw; + +/* function prototype */ +void atl1c_phy_disable(struct atl1c_hw *hw); +void atl1c_hw_set_mac_addr(struct atl1c_hw *hw); +int atl1c_phy_reset(struct atl1c_hw *hw); +int atl1c_read_mac_addr(struct atl1c_hw *hw); +int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex); +u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr); +void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value); +int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data); +int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data); +bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value); +int atl1c_phy_init(struct atl1c_hw *hw); +int atl1c_check_eeprom_exist(struct atl1c_hw *hw); +int atl1c_restart_autoneg(struct atl1c_hw *hw); + +/* register definition */ +#define REG_DEVICE_CAP 0x5C +#define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7 +#define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0 + +#define REG_DEVICE_CTRL 0x60 +#define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7 +#define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5 +#define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7 +#define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12 + +#define REG_LINK_CTRL 0x68 +#define LINK_CTRL_L0S_EN 0x01 +#define LINK_CTRL_L1_EN 0x02 + +#define REG_VPD_CAP 0x6C +#define VPD_CAP_ID_MASK 0xff +#define VPD_CAP_ID_SHIFT 0 +#define VPD_CAP_NEXT_PTR_MASK 0xFF +#define VPD_CAP_NEXT_PTR_SHIFT 8 +#define VPD_CAP_VPD_ADDR_MASK 0x7FFF +#define VPD_CAP_VPD_ADDR_SHIFT 16 +#define VPD_CAP_VPD_FLAG 0x80000000 + +#define REG_VPD_DATA 0x70 + +#define REG_PCIE_UC_SEVERITY 0x10C +#define PCIE_UC_SERVRITY_TRN 0x00000001 +#define PCIE_UC_SERVRITY_DLP 0x00000010 +#define PCIE_UC_SERVRITY_PSN_TLP 0x00001000 +#define PCIE_UC_SERVRITY_FCP 0x00002000 +#define PCIE_UC_SERVRITY_CPL_TO 0x00004000 +#define PCIE_UC_SERVRITY_CA 0x00008000 +#define PCIE_UC_SERVRITY_UC 0x00010000 +#define PCIE_UC_SERVRITY_ROV 0x00020000 +#define PCIE_UC_SERVRITY_MLFP 0x00040000 +#define PCIE_UC_SERVRITY_ECRC 0x00080000 +#define PCIE_UC_SERVRITY_UR 0x00100000 + +#define REG_DEV_SERIALNUM_CTRL 0x200 +#define REG_DEV_MAC_SEL_MASK 0x0 /* 0:EUI; 1:MAC */ +#define REG_DEV_MAC_SEL_SHIFT 0 +#define REG_DEV_SERIAL_NUM_EN_MASK 0x1 +#define REG_DEV_SERIAL_NUM_EN_SHIFT 1 + +#define REG_TWSI_CTRL 0x218 +#define TWSI_CTRL_LD_OFFSET_MASK 0xFF +#define TWSI_CTRL_LD_OFFSET_SHIFT 0 +#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 +#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 +#define TWSI_CTRL_SW_LDSTART 0x800 +#define TWSI_CTRL_HW_LDSTART 0x1000 +#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F +#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 +#define TWSI_CTRL_LD_EXIST 0x400000 +#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 +#define TWSI_CTRL_FREQ_SEL_100K 0 +#define TWSI_CTRL_FREQ_SEL_200K 1 +#define TWSI_CTRL_FREQ_SEL_300K 2 +#define TWSI_CTRL_FREQ_SEL_400K 3 +#define TWSI_CTRL_SMB_SLV_ADDR +#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 + + +#define REG_PCIE_DEV_MISC_CTRL 0x21C +#define PCIE_DEV_MISC_EXT_PIPE 0x2 +#define PCIE_DEV_MISC_RETRY_BUFDIS 0x1 +#define PCIE_DEV_MISC_SPIROM_EXIST 0x4 +#define PCIE_DEV_MISC_SERDES_ENDIAN 0x8 +#define PCIE_DEV_MISC_SERDES_SEL_DIN 0x10 + +#define REG_PCIE_PHYMISC 0x1000 +#define PCIE_PHYMISC_FORCE_RCV_DET 0x4 + +#define REG_TWSI_DEBUG 0x1108 +#define TWSI_DEBUG_DEV_EXIST 0x20000000 + +#define REG_EEPROM_CTRL 0x12C0 +#define EEPROM_CTRL_DATA_HI_MASK 0xFFFF +#define EEPROM_CTRL_DATA_HI_SHIFT 0 +#define EEPROM_CTRL_ADDR_MASK 0x3FF +#define EEPROM_CTRL_ADDR_SHIFT 16 +#define EEPROM_CTRL_ACK 0x40000000 +#define EEPROM_CTRL_RW 0x80000000 + +#define REG_EEPROM_DATA_LO 0x12C4 + +#define REG_OTP_CTRL 0x12F0 +#define OTP_CTRL_CLK_EN 0x0002 + +#define REG_PM_CTRL 0x12F8 +#define PM_CTRL_SDES_EN 0x00000001 +#define PM_CTRL_RBER_EN 0x00000002 +#define PM_CTRL_CLK_REQ_EN 0x00000004 +#define PM_CTRL_ASPM_L1_EN 0x00000008 +#define PM_CTRL_SERDES_L1_EN 0x00000010 +#define PM_CTRL_SERDES_PLL_L1_EN 0x00000020 +#define PM_CTRL_SERDES_PD_EX_L1 0x00000040 +#define PM_CTRL_SERDES_BUDS_RX_L1_EN 0x00000080 +#define PM_CTRL_L0S_ENTRY_TIMER_MASK 0xF +#define PM_CTRL_L0S_ENTRY_TIMER_SHIFT 8 +#define PM_CTRL_ASPM_L0S_EN 0x00001000 +#define PM_CTRL_CLK_SWH_L1 0x00002000 +#define PM_CTRL_CLK_PWM_VER1_1 0x00004000 +#define PM_CTRL_PCIE_RECV 0x00008000 +#define PM_CTRL_L1_ENTRY_TIMER_MASK 0xF +#define PM_CTRL_L1_ENTRY_TIMER_SHIFT 16 +#define PM_CTRL_PM_REQ_TIMER_MASK 0xF +#define PM_CTRL_PM_REQ_TIMER_SHIFT 20 +#define PM_CTRL_LCKDET_TIMER_MASK 0x3F +#define PM_CTRL_LCKDET_TIMER_SHIFT 24 +#define PM_CTRL_MAC_ASPM_CHK 0x40000000 +#define PM_CTRL_HOTRST 0x80000000 + +/* Selene Master Control Register */ +#define REG_MASTER_CTRL 0x1400 +#define MASTER_CTRL_SOFT_RST 0x1 +#define MASTER_CTRL_TEST_MODE_MASK 0x3 +#define MASTER_CTRL_TEST_MODE_SHIFT 2 +#define MASTER_CTRL_BERT_START 0x10 +#define MASTER_CTRL_MTIMER_EN 0x100 +#define MASTER_CTRL_MANUAL_INT 0x200 +#define MASTER_CTRL_TX_ITIMER_EN 0x400 +#define MASTER_CTRL_RX_ITIMER_EN 0x800 +#define MASTER_CTRL_CLK_SEL_DIS 0x1000 +#define MASTER_CTRL_CLK_SWH_MODE 0x2000 +#define MASTER_CTRL_INT_RDCLR 0x4000 +#define MASTER_CTRL_REV_NUM_SHIFT 16 +#define MASTER_CTRL_REV_NUM_MASK 0xff +#define MASTER_CTRL_DEV_ID_SHIFT 24 +#define MASTER_CTRL_DEV_ID_MASK 0x7f +#define MASTER_CTRL_OTP_SEL 0x80000000 + +/* Timer Initial Value Register */ +#define REG_MANUAL_TIMER_INIT 0x1404 + +/* IRQ ModeratorTimer Initial Value Register */ +#define REG_IRQ_MODRT_TIMER_INIT 0x1408 +#define IRQ_MODRT_TIMER_MASK 0xffff +#define IRQ_MODRT_TX_TIMER_SHIFT 0 +#define IRQ_MODRT_RX_TIMER_SHIFT 16 + +#define REG_GPHY_CTRL 0x140C +#define GPHY_CTRL_EXT_RESET 0x1 +#define GPHY_CTRL_RTL_MODE 0x2 +#define GPHY_CTRL_LED_MODE 0x4 +#define GPHY_CTRL_ANEG_NOW 0x8 +#define GPHY_CTRL_REV_ANEG 0x10 +#define GPHY_CTRL_GATE_25M_EN 0x20 +#define GPHY_CTRL_LPW_EXIT 0x40 +#define GPHY_CTRL_PHY_IDDQ 0x80 +#define GPHY_CTRL_PHY_IDDQ_DIS 0x100 +#define GPHY_CTRL_GIGA_DIS 0x200 +#define GPHY_CTRL_HIB_EN 0x400 +#define GPHY_CTRL_HIB_PULSE 0x800 +#define GPHY_CTRL_SEL_ANA_RST 0x1000 +#define GPHY_CTRL_PHY_PLL_ON 0x2000 +#define GPHY_CTRL_PWDOWN_HW 0x4000 +#define GPHY_CTRL_PHY_PLL_BYPASS 0x8000 + +#define GPHY_CTRL_DEFAULT ( \ + GPHY_CTRL_SEL_ANA_RST |\ + GPHY_CTRL_HIB_PULSE |\ + GPHY_CTRL_HIB_EN) + +#define GPHY_CTRL_PW_WOL_DIS ( \ + GPHY_CTRL_SEL_ANA_RST |\ + GPHY_CTRL_HIB_PULSE |\ + GPHY_CTRL_HIB_EN |\ + GPHY_CTRL_PWDOWN_HW |\ + GPHY_CTRL_PHY_IDDQ) + +/* Block IDLE Status Register */ +#define REG_IDLE_STATUS 0x1410 +#define IDLE_STATUS_MASK 0x00FF +#define IDLE_STATUS_RXMAC_NO_IDLE 0x1 +#define IDLE_STATUS_TXMAC_NO_IDLE 0x2 +#define IDLE_STATUS_RXQ_NO_IDLE 0x4 +#define IDLE_STATUS_TXQ_NO_IDLE 0x8 +#define IDLE_STATUS_DMAR_NO_IDLE 0x10 +#define IDLE_STATUS_DMAW_NO_IDLE 0x20 +#define IDLE_STATUS_SMB_NO_IDLE 0x40 +#define IDLE_STATUS_CMB_NO_IDLE 0x80 + +/* MDIO Control Register */ +#define REG_MDIO_CTRL 0x1414 +#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit + * control data to write to PHY + * MII management register */ +#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit + * status data that was read + * from the PHY MII management register */ +#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */ +#define MDIO_REG_ADDR_SHIFT 16 +#define MDIO_RW 0x200000 /* 1: read, 0: write */ +#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */ +#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO + * master. And this bit is self + * cleared after one cycle */ +#define MDIO_CLK_SEL_SHIFT 24 +#define MDIO_CLK_25_4 0 +#define MDIO_CLK_25_6 2 +#define MDIO_CLK_25_8 3 +#define MDIO_CLK_25_10 4 +#define MDIO_CLK_25_14 5 +#define MDIO_CLK_25_20 6 +#define MDIO_CLK_25_28 7 +#define MDIO_BUSY 0x8000000 +#define MDIO_AP_EN 0x10000000 +#define MDIO_WAIT_TIMES 10 + +/* MII PHY Status Register */ +#define REG_PHY_STATUS 0x1418 +#define PHY_GENERAL_STATUS_MASK 0xFFFF +#define PHY_STATUS_RECV_ENABLE 0x0001 +#define PHY_OE_PWSP_STATUS_MASK 0x07FF +#define PHY_OE_PWSP_STATUS_SHIFT 16 +#define PHY_STATUS_LPW_STATE 0x80000000 +/* BIST Control and Status Register0 (for the Packet Memory) */ +#define REG_BIST0_CTRL 0x141c +#define BIST0_NOW 0x1 +#define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is + * un-repairable because + * it has address decoder + * failure or more than 1 cell + * stuck-to-x failure */ +#define BIST0_FUSE_FLAG 0x4 + +/* BIST Control and Status Register1(for the retry buffer of PCI Express) */ +#define REG_BIST1_CTRL 0x1420 +#define BIST1_NOW 0x1 +#define BIST1_SRAM_FAIL 0x2 +#define BIST1_FUSE_FLAG 0x4 + +/* SerDes Lock Detect Control and Status Register */ +#define REG_SERDES_LOCK 0x1424 +#define SERDES_LOCK_DETECT 0x1 /* SerDes lock detected. This signal + * comes from Analog SerDes */ +#define SERDES_LOCK_DETECT_EN 0x2 /* 1: Enable SerDes Lock detect function */ + +/* MAC Control Register */ +#define REG_MAC_CTRL 0x1480 +#define MAC_CTRL_TX_EN 0x1 +#define MAC_CTRL_RX_EN 0x2 +#define MAC_CTRL_TX_FLOW 0x4 +#define MAC_CTRL_RX_FLOW 0x8 +#define MAC_CTRL_LOOPBACK 0x10 +#define MAC_CTRL_DUPLX 0x20 +#define MAC_CTRL_ADD_CRC 0x40 +#define MAC_CTRL_PAD 0x80 +#define MAC_CTRL_LENCHK 0x100 +#define MAC_CTRL_HUGE_EN 0x200 +#define MAC_CTRL_PRMLEN_SHIFT 10 +#define MAC_CTRL_PRMLEN_MASK 0xf +#define MAC_CTRL_RMV_VLAN 0x4000 +#define MAC_CTRL_PROMIS_EN 0x8000 +#define MAC_CTRL_TX_PAUSE 0x10000 +#define MAC_CTRL_SCNT 0x20000 +#define MAC_CTRL_SRST_TX 0x40000 +#define MAC_CTRL_TX_SIMURST 0x80000 +#define MAC_CTRL_SPEED_SHIFT 20 +#define MAC_CTRL_SPEED_MASK 0x3 +#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 +#define MAC_CTRL_TX_HUGE 0x800000 +#define MAC_CTRL_RX_CHKSUM_EN 0x1000000 +#define MAC_CTRL_MC_ALL_EN 0x2000000 +#define MAC_CTRL_BC_EN 0x4000000 +#define MAC_CTRL_DBG 0x8000000 +#define MAC_CTRL_SINGLE_PAUSE_EN 0x10000000 + +/* MAC IPG/IFG Control Register */ +#define REG_MAC_IPG_IFG 0x1484 +#define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back + * inter-packet gap. The + * default is 96-bit time */ +#define MAC_IPG_IFG_IPGT_MASK 0x7f +#define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to + * enforce in between RX frames */ +#define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */ +#define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */ +#define MAC_IPG_IFG_IPGR1_MASK 0x7f +#define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */ +#define MAC_IPG_IFG_IPGR2_MASK 0x7f + +/* MAC STATION ADDRESS */ +#define REG_MAC_STA_ADDR 0x1488 + +/* Hash table for multicast address */ +#define REG_RX_HASH_TABLE 0x1490 + +/* MAC Half-Duplex Control Register */ +#define REG_MAC_HALF_DUPLX_CTRL 0x1498 +#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */ +#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff +#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 +#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 +#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 +#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* No back-off on backpressure, + * immediately start the + * transmission after back pressure */ +#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */ +#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */ + +/* Maximum Frame Length Control Register */ +#define REG_MTU 0x149c + +/* Wake-On-Lan control register */ +#define REG_WOL_CTRL 0x14a0 +#define WOL_PATTERN_EN 0x00000001 +#define WOL_PATTERN_PME_EN 0x00000002 +#define WOL_MAGIC_EN 0x00000004 +#define WOL_MAGIC_PME_EN 0x00000008 +#define WOL_LINK_CHG_EN 0x00000010 +#define WOL_LINK_CHG_PME_EN 0x00000020 +#define WOL_PATTERN_ST 0x00000100 +#define WOL_MAGIC_ST 0x00000200 +#define WOL_LINKCHG_ST 0x00000400 +#define WOL_CLK_SWITCH_EN 0x00008000 +#define WOL_PT0_EN 0x00010000 +#define WOL_PT1_EN 0x00020000 +#define WOL_PT2_EN 0x00040000 +#define WOL_PT3_EN 0x00080000 +#define WOL_PT4_EN 0x00100000 +#define WOL_PT5_EN 0x00200000 +#define WOL_PT6_EN 0x00400000 + +/* WOL Length ( 2 DWORD ) */ +#define REG_WOL_PATTERN_LEN 0x14a4 +#define WOL_PT_LEN_MASK 0x7f +#define WOL_PT0_LEN_SHIFT 0 +#define WOL_PT1_LEN_SHIFT 8 +#define WOL_PT2_LEN_SHIFT 16 +#define WOL_PT3_LEN_SHIFT 24 +#define WOL_PT4_LEN_SHIFT 0 +#define WOL_PT5_LEN_SHIFT 8 +#define WOL_PT6_LEN_SHIFT 16 + +/* Internal SRAM Partition Register */ +#define RFDX_HEAD_ADDR_MASK 0x03FF +#define RFDX_HARD_ADDR_SHIFT 0 +#define RFDX_TAIL_ADDR_MASK 0x03FF +#define RFDX_TAIL_ADDR_SHIFT 16 + +#define REG_SRAM_RFD0_INFO 0x1500 +#define REG_SRAM_RFD1_INFO 0x1504 +#define REG_SRAM_RFD2_INFO 0x1508 +#define REG_SRAM_RFD3_INFO 0x150C + +#define REG_RFD_NIC_LEN 0x1510 /* In 8-bytes */ +#define RFD_NIC_LEN_MASK 0x03FF + +#define REG_SRAM_TRD_ADDR 0x1518 +#define TPD_HEAD_ADDR_MASK 0x03FF +#define TPD_HEAD_ADDR_SHIFT 0 +#define TPD_TAIL_ADDR_MASK 0x03FF +#define TPD_TAIL_ADDR_SHIFT 16 + +#define REG_SRAM_TRD_LEN 0x151C /* In 8-bytes */ +#define TPD_NIC_LEN_MASK 0x03FF + +#define REG_SRAM_RXF_ADDR 0x1520 +#define REG_SRAM_RXF_LEN 0x1524 +#define REG_SRAM_TXF_ADDR 0x1528 +#define REG_SRAM_TXF_LEN 0x152C +#define REG_SRAM_TCPH_ADDR 0x1530 +#define REG_SRAM_PKTH_ADDR 0x1532 + +/* + * Load Ptr Register + * Software sets this bit after the initialization of the head and tail */ +#define REG_LOAD_PTR 0x1534 + +/* + * addresses of all descriptors, as well as the following descriptor + * control register, which triggers each function block to load the head + * pointer to prepare for the operation. This bit is then self-cleared + * after one cycle. + */ +#define REG_RX_BASE_ADDR_HI 0x1540 +#define REG_TX_BASE_ADDR_HI 0x1544 +#define REG_SMB_BASE_ADDR_HI 0x1548 +#define REG_SMB_BASE_ADDR_LO 0x154C +#define REG_RFD0_HEAD_ADDR_LO 0x1550 +#define REG_RFD1_HEAD_ADDR_LO 0x1554 +#define REG_RFD2_HEAD_ADDR_LO 0x1558 +#define REG_RFD3_HEAD_ADDR_LO 0x155C +#define REG_RFD_RING_SIZE 0x1560 +#define RFD_RING_SIZE_MASK 0x0FFF +#define REG_RX_BUF_SIZE 0x1564 +#define RX_BUF_SIZE_MASK 0xFFFF +#define REG_RRD0_HEAD_ADDR_LO 0x1568 +#define REG_RRD1_HEAD_ADDR_LO 0x156C +#define REG_RRD2_HEAD_ADDR_LO 0x1570 +#define REG_RRD3_HEAD_ADDR_LO 0x1574 +#define REG_RRD_RING_SIZE 0x1578 +#define RRD_RING_SIZE_MASK 0x0FFF +#define REG_HTPD_HEAD_ADDR_LO 0x157C +#define REG_NTPD_HEAD_ADDR_LO 0x1580 +#define REG_TPD_RING_SIZE 0x1584 +#define TPD_RING_SIZE_MASK 0xFFFF +#define REG_CMB_BASE_ADDR_LO 0x1588 + +/* RSS about */ +#define REG_RSS_KEY0 0x14B0 +#define REG_RSS_KEY1 0x14B4 +#define REG_RSS_KEY2 0x14B8 +#define REG_RSS_KEY3 0x14BC +#define REG_RSS_KEY4 0x14C0 +#define REG_RSS_KEY5 0x14C4 +#define REG_RSS_KEY6 0x14C8 +#define REG_RSS_KEY7 0x14CC +#define REG_RSS_KEY8 0x14D0 +#define REG_RSS_KEY9 0x14D4 +#define REG_IDT_TABLE0 0x14E0 +#define REG_IDT_TABLE1 0x14E4 +#define REG_IDT_TABLE2 0x14E8 +#define REG_IDT_TABLE3 0x14EC +#define REG_IDT_TABLE4 0x14F0 +#define REG_IDT_TABLE5 0x14F4 +#define REG_IDT_TABLE6 0x14F8 +#define REG_IDT_TABLE7 0x14FC +#define REG_IDT_TABLE REG_IDT_TABLE0 +#define REG_RSS_HASH_VALUE 0x15B0 +#define REG_RSS_HASH_FLAG 0x15B4 +#define REG_BASE_CPU_NUMBER 0x15B8 + +/* TXQ Control Register */ +#define REG_TXQ_CTRL 0x1590 +#define TXQ_NUM_TPD_BURST_MASK 0xF +#define TXQ_NUM_TPD_BURST_SHIFT 0 +#define TXQ_CTRL_IP_OPTION_EN 0x10 +#define TXQ_CTRL_EN 0x20 +#define TXQ_CTRL_ENH_MODE 0x40 +#define TXQ_CTRL_LS_8023_EN 0x80 +#define TXQ_TXF_BURST_NUM_SHIFT 16 +#define TXQ_TXF_BURST_NUM_MASK 0xFFFF + +/* Jumbo packet Threshold for task offload */ +#define REG_TX_TSO_OFFLOAD_THRESH 0x1594 /* In 8-bytes */ +#define TX_TSO_OFFLOAD_THRESH_MASK 0x07FF + +#define REG_TXF_WATER_MARK 0x1598 /* In 8-bytes */ +#define TXF_WATER_MARK_MASK 0x0FFF +#define TXF_LOW_WATER_MARK_SHIFT 0 +#define TXF_HIGH_WATER_MARK_SHIFT 16 +#define TXQ_CTRL_BURST_MODE_EN 0x80000000 + +#define REG_THRUPUT_MON_CTRL 0x159C +#define THRUPUT_MON_RATE_MASK 0x3 +#define THRUPUT_MON_RATE_SHIFT 0 +#define THRUPUT_MON_EN 0x80 + +/* RXQ Control Register */ +#define REG_RXQ_CTRL 0x15A0 +#define ASPM_THRUPUT_LIMIT_MASK 0x3 +#define ASPM_THRUPUT_LIMIT_SHIFT 0 +#define ASPM_THRUPUT_LIMIT_NO 0x00 +#define ASPM_THRUPUT_LIMIT_1M 0x01 +#define ASPM_THRUPUT_LIMIT_10M 0x02 +#define ASPM_THRUPUT_LIMIT_100M 0x04 +#define RXQ1_CTRL_EN 0x10 +#define RXQ2_CTRL_EN 0x20 +#define RXQ3_CTRL_EN 0x40 +#define IPV6_CHKSUM_CTRL_EN 0x80 +#define RSS_HASH_BITS_MASK 0x00FF +#define RSS_HASH_BITS_SHIFT 8 +#define RSS_HASH_IPV4 0x10000 +#define RSS_HASH_IPV4_TCP 0x20000 +#define RSS_HASH_IPV6 0x40000 +#define RSS_HASH_IPV6_TCP 0x80000 +#define RXQ_RFD_BURST_NUM_MASK 0x003F +#define RXQ_RFD_BURST_NUM_SHIFT 20 +#define RSS_MODE_MASK 0x0003 +#define RSS_MODE_SHIFT 26 +#define RSS_NIP_QUEUE_SEL_MASK 0x1 +#define RSS_NIP_QUEUE_SEL_SHIFT 28 +#define RRS_HASH_CTRL_EN 0x20000000 +#define RX_CUT_THRU_EN 0x40000000 +#define RXQ_CTRL_EN 0x80000000 + +#define REG_RFD_FREE_THRESH 0x15A4 +#define RFD_FREE_THRESH_MASK 0x003F +#define RFD_FREE_HI_THRESH_SHIFT 0 +#define RFD_FREE_LO_THRESH_SHIFT 6 + +/* RXF flow control register */ +#define REG_RXQ_RXF_PAUSE_THRESH 0x15A8 +#define RXQ_RXF_PAUSE_TH_HI_SHIFT 0 +#define RXQ_RXF_PAUSE_TH_HI_MASK 0x0FFF +#define RXQ_RXF_PAUSE_TH_LO_SHIFT 16 +#define RXQ_RXF_PAUSE_TH_LO_MASK 0x0FFF + +#define REG_RXD_DMA_CTRL 0x15AC +#define RXD_DMA_THRESH_MASK 0x0FFF /* In 8-bytes */ +#define RXD_DMA_THRESH_SHIFT 0 +#define RXD_DMA_DOWN_TIMER_MASK 0xFFFF +#define RXD_DMA_DOWN_TIMER_SHIFT 16 + +/* DMA Engine Control Register */ +#define REG_DMA_CTRL 0x15C0 +#define DMA_CTRL_DMAR_IN_ORDER 0x1 +#define DMA_CTRL_DMAR_ENH_ORDER 0x2 +#define DMA_CTRL_DMAR_OUT_ORDER 0x4 +#define DMA_CTRL_RCB_VALUE 0x8 +#define DMA_CTRL_DMAR_BURST_LEN_MASK 0x0007 +#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4 +#define DMA_CTRL_DMAW_BURST_LEN_MASK 0x0007 +#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7 +#define DMA_CTRL_DMAR_REQ_PRI 0x400 +#define DMA_CTRL_DMAR_DLY_CNT_MASK 0x001F +#define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11 +#define DMA_CTRL_DMAW_DLY_CNT_MASK 0x000F +#define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16 +#define DMA_CTRL_CMB_EN 0x100000 +#define DMA_CTRL_SMB_EN 0x200000 +#define DMA_CTRL_CMB_NOW 0x400000 +#define MAC_CTRL_SMB_DIS 0x1000000 +#define DMA_CTRL_SMB_NOW 0x80000000 + +/* CMB/SMB Control Register */ +#define REG_SMB_STAT_TIMER 0x15C4 /* 2us resolution */ +#define SMB_STAT_TIMER_MASK 0xFFFFFF +#define REG_CMB_TPD_THRESH 0x15C8 +#define CMB_TPD_THRESH_MASK 0xFFFF +#define REG_CMB_TX_TIMER 0x15CC /* 2us resolution */ +#define CMB_TX_TIMER_MASK 0xFFFF + +/* Mail box */ +#define MB_RFDX_PROD_IDX_MASK 0xFFFF +#define REG_MB_RFD0_PROD_IDX 0x15E0 +#define REG_MB_RFD1_PROD_IDX 0x15E4 +#define REG_MB_RFD2_PROD_IDX 0x15E8 +#define REG_MB_RFD3_PROD_IDX 0x15EC + +#define MB_PRIO_PROD_IDX_MASK 0xFFFF +#define REG_MB_PRIO_PROD_IDX 0x15F0 +#define MB_HTPD_PROD_IDX_SHIFT 0 +#define MB_NTPD_PROD_IDX_SHIFT 16 + +#define MB_PRIO_CONS_IDX_MASK 0xFFFF +#define REG_MB_PRIO_CONS_IDX 0x15F4 +#define MB_HTPD_CONS_IDX_SHIFT 0 +#define MB_NTPD_CONS_IDX_SHIFT 16 + +#define REG_MB_RFD01_CONS_IDX 0x15F8 +#define MB_RFD0_CONS_IDX_MASK 0x0000FFFF +#define MB_RFD1_CONS_IDX_MASK 0xFFFF0000 +#define REG_MB_RFD23_CONS_IDX 0x15FC +#define MB_RFD2_CONS_IDX_MASK 0x0000FFFF +#define MB_RFD3_CONS_IDX_MASK 0xFFFF0000 + +/* Interrupt Status Register */ +#define REG_ISR 0x1600 +#define ISR_SMB 0x00000001 +#define ISR_TIMER 0x00000002 +/* + * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set + * in Table 51 Selene Master Control Register (Offset 0x1400). + */ +#define ISR_MANUAL 0x00000004 +#define ISR_HW_RXF_OV 0x00000008 /* RXF overflow interrupt */ +#define ISR_RFD0_UR 0x00000010 /* RFD0 under run */ +#define ISR_RFD1_UR 0x00000020 +#define ISR_RFD2_UR 0x00000040 +#define ISR_RFD3_UR 0x00000080 +#define ISR_TXF_UR 0x00000100 +#define ISR_DMAR_TO_RST 0x00000200 +#define ISR_DMAW_TO_RST 0x00000400 +#define ISR_TX_CREDIT 0x00000800 +#define ISR_GPHY 0x00001000 +/* GPHY low power state interrupt */ +#define ISR_GPHY_LPW 0x00002000 +#define ISR_TXQ_TO_RST 0x00004000 +#define ISR_TX_PKT 0x00008000 +#define ISR_RX_PKT_0 0x00010000 +#define ISR_RX_PKT_1 0x00020000 +#define ISR_RX_PKT_2 0x00040000 +#define ISR_RX_PKT_3 0x00080000 +#define ISR_MAC_RX 0x00100000 +#define ISR_MAC_TX 0x00200000 +#define ISR_UR_DETECTED 0x00400000 +#define ISR_FERR_DETECTED 0x00800000 +#define ISR_NFERR_DETECTED 0x01000000 +#define ISR_CERR_DETECTED 0x02000000 +#define ISR_PHY_LINKDOWN 0x04000000 +#define ISR_DIS_INT 0x80000000 + +/* Interrupt Mask Register */ +#define REG_IMR 0x1604 + +#define IMR_NORMAL_MASK (\ + ISR_MANUAL |\ + ISR_HW_RXF_OV |\ + ISR_RFD0_UR |\ + ISR_TXF_UR |\ + ISR_DMAR_TO_RST |\ + ISR_TXQ_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_GPHY |\ + ISR_TX_PKT |\ + ISR_RX_PKT_0 |\ + ISR_GPHY_LPW |\ + ISR_PHY_LINKDOWN) + +#define ISR_RX_PKT (\ + ISR_RX_PKT_0 |\ + ISR_RX_PKT_1 |\ + ISR_RX_PKT_2 |\ + ISR_RX_PKT_3) + +#define ISR_OVER (\ + ISR_RFD0_UR |\ + ISR_RFD1_UR |\ + ISR_RFD2_UR |\ + ISR_RFD3_UR |\ + ISR_HW_RXF_OV |\ + ISR_TXF_UR) + +#define ISR_ERROR (\ + ISR_DMAR_TO_RST |\ + ISR_TXQ_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_PHY_LINKDOWN) + +#define REG_INT_RETRIG_TIMER 0x1608 +#define INT_RETRIG_TIMER_MASK 0xFFFF + +#define REG_HDS_CTRL 0x160C +#define HDS_CTRL_EN 0x0001 +#define HDS_CTRL_BACKFILLSIZE_SHIFT 8 +#define HDS_CTRL_BACKFILLSIZE_MASK 0x0FFF +#define HDS_CTRL_MAX_HDRSIZE_SHIFT 20 +#define HDS_CTRL_MAC_HDRSIZE_MASK 0x0FFF + +#define REG_MAC_RX_STATUS_BIN 0x1700 +#define REG_MAC_RX_STATUS_END 0x175c +#define REG_MAC_TX_STATUS_BIN 0x1760 +#define REG_MAC_TX_STATUS_END 0x17c0 + +/* DEBUG ADDR */ +#define REG_DEBUG_DATA0 0x1900 +#define REG_DEBUG_DATA1 0x1904 + +/* PHY Control Register */ +#define MII_BMCR 0x00 +#define BMCR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define BMCR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define BMCR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define BMCR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define BMCR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define BMCR_POWER_DOWN 0x0800 /* Power down */ +#define BMCR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define BMCR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define BMCR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define BMCR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define BMCR_SPEED_MASK 0x2040 +#define BMCR_SPEED_1000 0x0040 +#define BMCR_SPEED_100 0x2000 +#define BMCR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_BMSR 0x01 +#define BMMSR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define BMSR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define BMSR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define BMSR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define BMSR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define BMSR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define BMSR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define BMSR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define BMSR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define BMSR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define BMSR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define BMSR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define BMSR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define BMMII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define BMMII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +#define MII_PHYSID1 0x02 +#define MII_PHYSID2 0x03 + +/* Autoneg Advertisement Register */ +#define MII_ADVERTISE 0x04 +#define ADVERTISE_SPEED_MASK 0x01E0 +#define ADVERTISE_DEFAULT_CAP 0x0DE0 + +/* 1000BASE-T Control Register */ +#define MII_GIGA_CR 0x09 +#define GIGA_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port 0=DTE device */ + +#define GIGA_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master 0=Configure PHY as Slave */ +#define GIGA_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value 0=Automatic Master/Slave config */ +#define GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define GIGA_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define GIGA_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define GIGA_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define GIGA_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ +#define GIGA_CR_1000T_SPEED_MASK 0x0300 +#define GIGA_CR_1000T_DEFAULT_CAP 0x0300 + +/* PHY Specific Status Register */ +#define MII_GIGA_PSSR 0x11 +#define GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define GIGA_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define GIGA_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define GIGA_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define GIGA_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define GIGA_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +/* PHY Interrupt Enable Register */ +#define MII_IER 0x12 +#define IER_LINK_UP 0x0400 +#define IER_LINK_DOWN 0x0800 + +/* PHY Interrupt Status Register */ +#define MII_ISR 0x13 +#define ISR_LINK_UP 0x0400 +#define ISR_LINK_DOWN 0x0800 + +/* Cable-Detect-Test Control Register */ +#define MII_CDTC 0x16 +#define CDTC_EN_OFF 0 /* sc */ +#define CDTC_EN_BITS 1 +#define CDTC_PAIR_OFF 8 +#define CDTC_PAIR_BIT 2 + +/* Cable-Detect-Test Status Register */ +#define MII_CDTS 0x1C +#define CDTS_STATUS_OFF 8 +#define CDTS_STATUS_BITS 2 +#define CDTS_STATUS_NORMAL 0 +#define CDTS_STATUS_SHORT 1 +#define CDTS_STATUS_OPEN 2 +#define CDTS_STATUS_INVALID 3 + +#define MII_DBG_ADDR 0x1D +#define MII_DBG_DATA 0x1E + +#define MII_ANA_CTRL_0 0x0 +#define ANA_RESTART_CAL 0x0001 +#define ANA_MANUL_SWICH_ON_SHIFT 0x1 +#define ANA_MANUL_SWICH_ON_MASK 0xF +#define ANA_MAN_ENABLE 0x0020 +#define ANA_SEL_HSP 0x0040 +#define ANA_EN_HB 0x0080 +#define ANA_EN_HBIAS 0x0100 +#define ANA_OEN_125M 0x0200 +#define ANA_EN_LCKDT 0x0400 +#define ANA_LCKDT_PHY 0x0800 +#define ANA_AFE_MODE 0x1000 +#define ANA_VCO_SLOW 0x2000 +#define ANA_VCO_FAST 0x4000 +#define ANA_SEL_CLK125M_DSP 0x8000 + +#define MII_ANA_CTRL_4 0x4 +#define ANA_IECHO_ADJ_MASK 0xF +#define ANA_IECHO_ADJ_3_SHIFT 0 +#define ANA_IECHO_ADJ_2_SHIFT 4 +#define ANA_IECHO_ADJ_1_SHIFT 8 +#define ANA_IECHO_ADJ_0_SHIFT 12 + +#define MII_ANA_CTRL_5 0x5 +#define ANA_SERDES_CDR_BW_SHIFT 0 +#define ANA_SERDES_CDR_BW_MASK 0x3 +#define ANA_MS_PAD_DBG 0x0004 +#define ANA_SPEEDUP_DBG 0x0008 +#define ANA_SERDES_TH_LOS_SHIFT 4 +#define ANA_SERDES_TH_LOS_MASK 0x3 +#define ANA_SERDES_EN_DEEM 0x0040 +#define ANA_SERDES_TXELECIDLE 0x0080 +#define ANA_SERDES_BEACON 0x0100 +#define ANA_SERDES_HALFTXDR 0x0200 +#define ANA_SERDES_SEL_HSP 0x0400 +#define ANA_SERDES_EN_PLL 0x0800 +#define ANA_SERDES_EN 0x1000 +#define ANA_SERDES_EN_LCKDT 0x2000 + +#define MII_ANA_CTRL_11 0xB +#define ANA_PS_HIB_EN 0x8000 + +#define MII_ANA_CTRL_18 0x12 +#define ANA_TEST_MODE_10BT_01SHIFT 0 +#define ANA_TEST_MODE_10BT_01MASK 0x3 +#define ANA_LOOP_SEL_10BT 0x0004 +#define ANA_RGMII_MODE_SW 0x0008 +#define ANA_EN_LONGECABLE 0x0010 +#define ANA_TEST_MODE_10BT_2 0x0020 +#define ANA_EN_10BT_IDLE 0x0400 +#define ANA_EN_MASK_TB 0x0800 +#define ANA_TRIGGER_SEL_TIMER_SHIFT 12 +#define ANA_TRIGGER_SEL_TIMER_MASK 0x3 +#define ANA_INTERVAL_SEL_TIMER_SHIFT 14 +#define ANA_INTERVAL_SEL_TIMER_MASK 0x3 + +#define MII_ANA_CTRL_41 0x29 +#define ANA_TOP_PS_EN 0x8000 + +#define MII_ANA_CTRL_54 0x36 +#define ANA_LONG_CABLE_TH_100_SHIFT 0 +#define ANA_LONG_CABLE_TH_100_MASK 0x3F +#define ANA_DESERVED 0x0040 +#define ANA_EN_LIT_CH 0x0080 +#define ANA_SHORT_CABLE_TH_100_SHIFT 8 +#define ANA_SHORT_CABLE_TH_100_MASK 0x3F +#define ANA_BP_BAD_LINK_ACCUM 0x4000 +#define ANA_BP_SMALL_BW 0x8000 + +#endif /*_ATL1C_HW_H_*/ diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c new file mode 100644 index 000000000000..deb7b53167ee --- /dev/null +++ b/drivers/net/atl1c/atl1c_main.c @@ -0,0 +1,2797 @@ +/* + * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "atl1c.h" + +#define ATL1C_DRV_VERSION "1.0.0.1-NAPI" +char atl1c_driver_name[] = "atl1c"; +char atl1c_driver_version[] = ATL1C_DRV_VERSION; +#define PCI_DEVICE_ID_ATTANSIC_L2C 0x1062 +#define PCI_DEVICE_ID_ATTANSIC_L1C 0x1063 +/* + * atl1c_pci_tbl - PCI Device ID Table + * + * Wildcard entries (PCI_ANY_ID) should come last + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, private data (not used) } + */ +static struct pci_device_id atl1c_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1C)}, + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2C)}, + /* required last entry */ + { 0 } +}; +MODULE_DEVICE_TABLE(pci, atl1c_pci_tbl); + +MODULE_AUTHOR("Jie Yang <jie.yang@atheros.com>"); +MODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ATL1C_DRV_VERSION); + +static int atl1c_stop_mac(struct atl1c_hw *hw); +static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw); +static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw); +static void atl1c_disable_l0s_l1(struct atl1c_hw *hw); +static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup); +static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter); +static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, + int *work_done, int work_to_do); + +static const u16 atl1c_pay_load_size[] = { + 128, 256, 512, 1024, 2048, 4096, +}; + +static const u16 atl1c_rfd_prod_idx_regs[AT_MAX_RECEIVE_QUEUE] = +{ + REG_MB_RFD0_PROD_IDX, + REG_MB_RFD1_PROD_IDX, + REG_MB_RFD2_PROD_IDX, + REG_MB_RFD3_PROD_IDX +}; + +static const u16 atl1c_rfd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] = +{ + REG_RFD0_HEAD_ADDR_LO, + REG_RFD1_HEAD_ADDR_LO, + REG_RFD2_HEAD_ADDR_LO, + REG_RFD3_HEAD_ADDR_LO +}; + +static const u16 atl1c_rrd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] = +{ + REG_RRD0_HEAD_ADDR_LO, + REG_RRD1_HEAD_ADDR_LO, + REG_RRD2_HEAD_ADDR_LO, + REG_RRD3_HEAD_ADDR_LO +}; + +static const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP; + +/* + * atl1c_init_pcie - init PCIE module + */ +static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag) +{ + u32 data; + u32 pci_cmd; + struct pci_dev *pdev = hw->adapter->pdev; + + AT_READ_REG(hw, PCI_COMMAND, &pci_cmd); + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_IO); + AT_WRITE_REG(hw, PCI_COMMAND, pci_cmd); + + /* + * Clear any PowerSaveing Settings + */ + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + /* + * Mask some pcie error bits + */ + AT_READ_REG(hw, REG_PCIE_UC_SEVERITY, &data); + data &= ~PCIE_UC_SERVRITY_DLP; + data &= ~PCIE_UC_SERVRITY_FCP; + AT_WRITE_REG(hw, REG_PCIE_UC_SEVERITY, data); + + if (flag & ATL1C_PCIE_L0S_L1_DISABLE) + atl1c_disable_l0s_l1(hw); + if (flag & ATL1C_PCIE_PHY_RESET) + AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT); + else + AT_WRITE_REG(hw, REG_GPHY_CTRL, + GPHY_CTRL_DEFAULT | GPHY_CTRL_EXT_RESET); + + msleep(1); +} + +/* + * atl1c_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + */ +static inline void atl1c_irq_enable(struct atl1c_adapter *adapter) +{ + if (likely(atomic_dec_and_test(&adapter->irq_sem))) { + AT_WRITE_REG(&adapter->hw, REG_ISR, 0x7FFFFFFF); + AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask); + AT_WRITE_FLUSH(&adapter->hw); + } +} + +/* + * atl1c_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + */ +static inline void atl1c_irq_disable(struct atl1c_adapter *adapter) +{ + atomic_inc(&adapter->irq_sem); + AT_WRITE_REG(&adapter->hw, REG_IMR, 0); + AT_WRITE_FLUSH(&adapter->hw); + synchronize_irq(adapter->pdev->irq); +} + +/* + * atl1c_irq_reset - reset interrupt confiure on the NIC + * @adapter: board private structure + */ +static inline void atl1c_irq_reset(struct atl1c_adapter *adapter) +{ + atomic_set(&adapter->irq_sem, 1); + atl1c_irq_enable(adapter); +} + +/* + * atl1c_phy_config - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + */ +static void atl1c_phy_config(unsigned long data) +{ + struct atl1c_adapter *adapter = (struct atl1c_adapter *) data; + struct atl1c_hw *hw = &adapter->hw; + unsigned long flags; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + atl1c_restart_autoneg(hw); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); +} + +void atl1c_reinit_locked(struct atl1c_adapter *adapter) +{ + + WARN_ON(in_interrupt()); + atl1c_down(adapter); + atl1c_up(adapter); + clear_bit(__AT_RESETTING, &adapter->flags); +} + +static void atl1c_reset_task(struct work_struct *work) +{ + struct atl1c_adapter *adapter; + struct net_device *netdev; + + adapter = container_of(work, struct atl1c_adapter, reset_task); + netdev = adapter->netdev; + + netif_device_detach(netdev); + atl1c_down(adapter); + atl1c_up(adapter); + netif_device_attach(netdev); +} + +static void atl1c_check_link_status(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + int err; + unsigned long flags; + u16 speed, duplex, phy_data; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + /* MII_BMSR must read twise */ + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); + + if ((phy_data & BMSR_LSTATUS) == 0) { + /* link down */ + if (netif_carrier_ok(netdev)) { + hw->hibernate = true; + atl1c_set_aspm(hw, false); + if (atl1c_stop_mac(hw) != 0) + if (netif_msg_hw(adapter)) + dev_warn(&pdev->dev, + "stop mac failed\n"); + } + netif_carrier_off(netdev); + } else { + /* Link Up */ + hw->hibernate = false; + spin_lock_irqsave(&adapter->mdio_lock, flags); + err = atl1c_get_speed_and_duplex(hw, &speed, &duplex); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); + if (unlikely(err)) + return; + /* link result is our setting */ + if (adapter->link_speed != speed || + adapter->link_duplex != duplex) { + adapter->link_speed = speed; + adapter->link_duplex = duplex; + atl1c_enable_tx_ctrl(hw); + atl1c_enable_rx_ctrl(hw); + atl1c_setup_mac_ctrl(adapter); + atl1c_set_aspm(hw, true); + if (netif_msg_link(adapter)) + dev_info(&pdev->dev, + "%s: %s NIC Link is Up<%d Mbps %s>\n", + atl1c_driver_name, netdev->name, + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? + "Full Duplex" : "Half Duplex"); + } + if (!netif_carrier_ok(netdev)) + netif_carrier_on(netdev); + } +} + +/* + * atl1c_link_chg_task - deal with link change event Out of interrupt context + * @netdev: network interface device structure + */ +static void atl1c_link_chg_task(struct work_struct *work) +{ + struct atl1c_adapter *adapter; + + adapter = container_of(work, struct atl1c_adapter, link_chg_task); + atl1c_check_link_status(adapter); +} + +static void atl1c_link_chg_event(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + u16 phy_data; + u16 link_up; + + spin_lock(&adapter->mdio_lock); + atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + spin_unlock(&adapter->mdio_lock); + link_up = phy_data & BMSR_LSTATUS; + /* notify upper layer link down ASAP */ + if (!link_up) { + if (netif_carrier_ok(netdev)) { + /* old link state: Up */ + netif_carrier_off(netdev); + if (netif_msg_link(adapter)) + dev_info(&pdev->dev, + "%s: %s NIC Link is Down\n", + atl1c_driver_name, netdev->name); + adapter->link_speed = SPEED_0; + } + } + schedule_work(&adapter->link_chg_task); +} + +static void atl1c_del_timer(struct atl1c_adapter *adapter) +{ + del_timer_sync(&adapter->phy_config_timer); +} + +static void atl1c_cancel_work(struct atl1c_adapter *adapter) +{ + cancel_work_sync(&adapter->reset_task); + cancel_work_sync(&adapter->link_chg_task); +} + +/* + * atl1c_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + */ +static void atl1c_tx_timeout(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->reset_task); +} + +/* + * atl1c_set_multi - Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_multi entry point is called whenever the multicast address + * list or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper multicast, + * promiscuous mode, and all-multi behavior. + */ +static void atl1c_set_multi(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + struct dev_mc_list *mc_ptr; + u32 mac_ctrl_data; + u32 hash_value; + + /* Check for Promiscuous and All Multicast modes */ + AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data); + + if (netdev->flags & IFF_PROMISC) { + mac_ctrl_data |= MAC_CTRL_PROMIS_EN; + } else if (netdev->flags & IFF_ALLMULTI) { + mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; + mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN; + } else { + mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); + } + + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); + + /* clear the old settings from the multicast hash table */ + AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + /* comoute mc addresses' hash value ,and put it into hash table */ + for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { + hash_value = atl1c_hash_mc_addr(hw, mc_ptr->dmi_addr); + atl1c_hash_set(hw, hash_value); + } +} + +static void atl1c_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + u32 mac_ctrl_data = 0; + + if (netif_msg_pktdata(adapter)) + dev_dbg(&pdev->dev, "atl1c_vlan_rx_register\n"); + + atl1c_irq_disable(adapter); + + adapter->vlgrp = grp; + AT_READ_REG(&adapter->hw, REG_MAC_CTRL, &mac_ctrl_data); + + if (grp) { + /* enable VLAN tag insert/strip */ + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + } else { + /* disable VLAN tag insert/strip */ + mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN; + } + + AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); + atl1c_irq_enable(adapter); +} + +static void atl1c_restore_vlan(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + if (netif_msg_pktdata(adapter)) + dev_dbg(&pdev->dev, "atl1c_restore_vlan !"); + atl1c_vlan_rx_register(adapter->netdev, adapter->vlgrp); +} +/* + * atl1c_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + */ +static int atl1c_set_mac_addr(struct net_device *netdev, void *p) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (netif_running(netdev)) + return -EBUSY; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); + + atl1c_hw_set_mac_addr(&adapter->hw); + + return 0; +} + +static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter, + struct net_device *dev) +{ + int mtu = dev->mtu; + + adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ? + roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE; +} +/* + * atl1c_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + */ +static int atl1c_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + int old_mtu = netdev->mtu; + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || + (max_frame > MAX_JUMBO_FRAME_SIZE)) { + if (netif_msg_link(adapter)) + dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); + return -EINVAL; + } + /* set MTU */ + if (old_mtu != new_mtu && netif_running(netdev)) { + while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) + msleep(1); + netdev->mtu = new_mtu; + adapter->hw.max_frame_size = new_mtu; + atl1c_set_rxbufsize(adapter, netdev); + atl1c_down(adapter); + atl1c_up(adapter); + clear_bit(__AT_RESETTING, &adapter->flags); + if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) { + u32 phy_data; + + AT_READ_REG(&adapter->hw, 0x1414, &phy_data); + phy_data |= 0x10000000; + AT_WRITE_REG(&adapter->hw, 0x1414, phy_data); + } + + } + return 0; +} + +/* + * caller should hold mdio_lock + */ +static int atl1c_mdio_read(struct net_device *netdev, int phy_id, int reg_num) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + u16 result; + + atl1c_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result); + return result; +} + +static void atl1c_mdio_write(struct net_device *netdev, int phy_id, + int reg_num, int val) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + atl1c_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val); +} + +/* + * atl1c_mii_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl1c_mii_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + struct mii_ioctl_data *data = if_mii(ifr); + unsigned long flags; + int retval = 0; + + if (!netif_running(netdev)) + return -EINVAL; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 0; + break; + + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) { + retval = -EPERM; + goto out; + } + if (atl1c_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, + &data->val_out)) { + retval = -EIO; + goto out; + } + break; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) { + retval = -EPERM; + goto out; + } + if (data->reg_num & ~(0x1F)) { + retval = -EFAULT; + goto out; + } + + dev_dbg(&pdev->dev, "<atl1c_mii_ioctl> write %x %x", + data->reg_num, data->val_in); + if (atl1c_write_phy_reg(&adapter->hw, + data->reg_num, data->val_in)) { + retval = -EIO; + goto out; + } + break; + + default: + retval = -EOPNOTSUPP; + break; + } +out: + spin_unlock_irqrestore(&adapter->mdio_lock, flags); + return retval; +} + +/* + * atl1c_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl1c_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return atl1c_mii_ioctl(netdev, ifr, cmd); + default: + return -EOPNOTSUPP; + } +} + +/* + * atl1c_alloc_queues - Allocate memory for all rings + * @adapter: board private structure to initialize + * + */ +static int __devinit atl1c_alloc_queues(struct atl1c_adapter *adapter) +{ + return 0; +} + +static void atl1c_set_mac_type(struct atl1c_hw *hw) +{ + switch (hw->device_id) { + case PCI_DEVICE_ID_ATTANSIC_L2C: + hw->nic_type = athr_l2c; + break; + + case PCI_DEVICE_ID_ATTANSIC_L1C: + hw->nic_type = athr_l1c; + break; + + default: + break; + } +} + +static int atl1c_setup_mac_funcs(struct atl1c_hw *hw) +{ + u32 phy_status_data; + u32 link_ctrl_data; + + atl1c_set_mac_type(hw); + AT_READ_REG(hw, REG_PHY_STATUS, &phy_status_data); + AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data); + + hw->ctrl_flags = ATL1C_INTR_CLEAR_ON_READ | + ATL1C_INTR_MODRT_ENABLE | + ATL1C_RX_IPV6_CHKSUM | + ATL1C_TXQ_MODE_ENHANCE; + if (link_ctrl_data & LINK_CTRL_L0S_EN) + hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT; + if (link_ctrl_data & LINK_CTRL_L1_EN) + hw->ctrl_flags |= ATL1C_ASPM_L1_SUPPORT; + + if (hw->nic_type == athr_l1c) { + hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON; + hw->ctrl_flags |= ATL1C_LINK_CAP_1000M; + } + return 0; +} +/* + * atl1c_sw_init - Initialize general software structures (struct atl1c_adapter) + * @adapter: board private structure to initialize + * + * atl1c_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + */ +static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; + + adapter->wol = 0; + adapter->link_speed = SPEED_0; + adapter->link_duplex = FULL_DUPLEX; + adapter->num_rx_queues = AT_DEF_RECEIVE_QUEUE; + adapter->tpd_ring[0].count = 1024; + adapter->rfd_ring[0].count = 512; + + hw->vendor_id = pdev->vendor; + hw->device_id = pdev->device; + hw->subsystem_vendor_id = pdev->subsystem_vendor; + hw->subsystem_id = pdev->subsystem_device; + + /* before link up, we assume hibernate is true */ + hw->hibernate = true; + hw->media_type = MEDIA_TYPE_AUTO_SENSOR; + if (atl1c_setup_mac_funcs(hw) != 0) { + dev_err(&pdev->dev, "set mac function pointers failed\n"); + return -1; + } + hw->intr_mask = IMR_NORMAL_MASK; + hw->phy_configured = false; + hw->preamble_len = 7; + hw->max_frame_size = adapter->netdev->mtu; + if (adapter->num_rx_queues < 2) { + hw->rss_type = atl1c_rss_disable; + hw->rss_mode = atl1c_rss_mode_disable; + } else { + hw->rss_type = atl1c_rss_ipv4; + hw->rss_mode = atl1c_rss_mul_que_mul_int; + hw->rss_hash_bits = 16; + } + hw->autoneg_advertised = ADVERTISED_Autoneg; + hw->indirect_tab = 0xE4E4E4E4; + hw->base_cpu = 0; + + hw->ict = 50000; /* 100ms */ + hw->smb_timer = 200000; /* 400ms */ + hw->cmb_tpd = 4; + hw->cmb_tx_timer = 1; /* 2 us */ + hw->rx_imt = 200; + hw->tx_imt = 1000; + + hw->tpd_burst = 5; + hw->rfd_burst = 8; + hw->dma_order = atl1c_dma_ord_out; + hw->dmar_block = atl1c_dma_req_1024; + hw->dmaw_block = atl1c_dma_req_1024; + hw->dmar_dly_cnt = 15; + hw->dmaw_dly_cnt = 4; + + if (atl1c_alloc_queues(adapter)) { + dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); + return -ENOMEM; + } + /* TODO */ + atl1c_set_rxbufsize(adapter, adapter->netdev); + atomic_set(&adapter->irq_sem, 1); + spin_lock_init(&adapter->mdio_lock); + spin_lock_init(&adapter->tx_lock); + set_bit(__AT_DOWN, &adapter->flags); + + return 0; +} + +/* + * atl1c_clean_tx_ring - Free Tx-skb + * @adapter: board private structure + */ +static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + struct atl1c_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; + u16 index, ring_count; + + ring_count = tpd_ring->count; + for (index = 0; index < ring_count; index++) { + buffer_info = &tpd_ring->buffer_info[index]; + if (buffer_info->state == ATL1_BUFFER_FREE) + continue; + if (buffer_info->dma) + pci_unmap_single(pdev, buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + if (buffer_info->skb) + dev_kfree_skb(buffer_info->skb); + buffer_info->dma = 0; + buffer_info->skb = NULL; + buffer_info->state = ATL1_BUFFER_FREE; + } + + /* Zero out Tx-buffers */ + memset(tpd_ring->desc, 0, sizeof(struct atl1c_tpd_desc) * + ring_count); + atomic_set(&tpd_ring->next_to_clean, 0); + tpd_ring->next_to_use = 0; +} + +/* + * atl1c_clean_rx_ring - Free rx-reservation skbs + * @adapter: board private structure + */ +static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter) +{ + struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; + struct atl1c_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; + int i, j; + + for (i = 0; i < adapter->num_rx_queues; i++) { + for (j = 0; j < rfd_ring[i].count; j++) { + buffer_info = &rfd_ring[i].buffer_info[j]; + if (buffer_info->state == ATL1_BUFFER_FREE) + continue; + if (buffer_info->dma) + pci_unmap_single(pdev, buffer_info->dma, + buffer_info->length, + PCI_DMA_FROMDEVICE); + if (buffer_info->skb) + dev_kfree_skb(buffer_info->skb); + buffer_info->state = ATL1_BUFFER_FREE; + buffer_info->skb = NULL; + } + /* zero out the descriptor ring */ + memset(rfd_ring[i].desc, 0, rfd_ring[i].size); + rfd_ring[i].next_to_clean = 0; + rfd_ring[i].next_to_use = 0; + rrd_ring[i].next_to_use = 0; + rrd_ring[i].next_to_clean = 0; + } +} + +/* + * Read / Write Ptr Initialize: + */ +static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter) +{ + struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; + struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; + struct atl1c_buffer *buffer_info; + int i, j; + + for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { + tpd_ring[i].next_to_use = 0; + atomic_set(&tpd_ring[i].next_to_clean, 0); + buffer_info = tpd_ring[i].buffer_info; + for (j = 0; j < tpd_ring->count; j++) + buffer_info[i].state = ATL1_BUFFER_FREE; + } + for (i = 0; i < adapter->num_rx_queues; i++) { + rfd_ring[i].next_to_use = 0; + rfd_ring[i].next_to_clean = 0; + rrd_ring[i].next_to_use = 0; + rrd_ring[i].next_to_clean = 0; + for (j = 0; j < rfd_ring[i].count; j++) { + buffer_info = &rfd_ring[i].buffer_info[j]; + buffer_info->state = ATL1_BUFFER_FREE; + } + } +} + +/* + * atl1c_free_ring_resources - Free Tx / RX descriptor Resources + * @adapter: board private structure + * + * Free all transmit software resources + */ +static void atl1c_free_ring_resources(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + pci_free_consistent(pdev, adapter->ring_header.size, + adapter->ring_header.desc, + adapter->ring_header.dma); + adapter->ring_header.desc = NULL; + + /* Note: just free tdp_ring.buffer_info, + * it contain rfd_ring.buffer_info, do not double free */ + if (adapter->tpd_ring[0].buffer_info) { + kfree(adapter->tpd_ring[0].buffer_info); + adapter->tpd_ring[0].buffer_info = NULL; + } +} + +/* + * atl1c_setup_mem_resources - allocate Tx / RX descriptor resources + * @adapter: board private structure + * + * Return 0 on success, negative on failure + */ +static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; + struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; + struct atl1c_ring_header *ring_header = &adapter->ring_header; + int num_rx_queues = adapter->num_rx_queues; + int size; + int i; + int count = 0; + int rx_desc_count = 0; + u32 offset = 0; + + rrd_ring[0].count = rfd_ring[0].count; + for (i = 1; i < AT_MAX_TRANSMIT_QUEUE; i++) + tpd_ring[i].count = tpd_ring[0].count; + + for (i = 1; i < adapter->num_rx_queues; i++) + rfd_ring[i].count = rrd_ring[i].count = rfd_ring[0].count; + + /* 2 tpd queue, one high priority queue, + * another normal priority queue */ + size = sizeof(struct atl1c_buffer) * (tpd_ring->count * 2 + + rfd_ring->count * num_rx_queues); + tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL); + if (unlikely(!tpd_ring->buffer_info)) { + dev_err(&pdev->dev, "kzalloc failed, size = %d\n", + size); + goto err_nomem; + } + for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { + tpd_ring[i].buffer_info = + (struct atl1c_buffer *) (tpd_ring->buffer_info + count); + count += tpd_ring[i].count; + } + + for (i = 0; i < num_rx_queues; i++) { + rfd_ring[i].buffer_info = + (struct atl1c_buffer *) (tpd_ring->buffer_info + count); + count += rfd_ring[i].count; + rx_desc_count += rfd_ring[i].count; + } + /* + * real ring DMA buffer + * each ring/block may need up to 8 bytes for alignment, hence the + * additional bytes tacked onto the end. + */ + ring_header->size = size = + sizeof(struct atl1c_tpd_desc) * tpd_ring->count * 2 + + sizeof(struct atl1c_rx_free_desc) * rx_desc_count + + sizeof(struct atl1c_recv_ret_status) * rx_desc_count + + sizeof(struct atl1c_hw_stats) + + 8 * 4 + 8 * 2 * num_rx_queues; + + ring_header->desc = pci_alloc_consistent(pdev, ring_header->size, + &ring_header->dma); + if (unlikely(!ring_header->desc)) { + dev_err(&pdev->dev, "pci_alloc_consistend failed\n"); + goto err_nomem; + } + memset(ring_header->desc, 0, ring_header->size); + /* init TPD ring */ + + tpd_ring[0].dma = roundup(ring_header->dma, 8); + offset = tpd_ring[0].dma - ring_header->dma; + for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { + tpd_ring[i].dma = ring_header->dma + offset; + tpd_ring[i].desc = (u8 *) ring_header->desc + offset; + tpd_ring[i].size = + sizeof(struct atl1c_tpd_desc) * tpd_ring[i].count; + offset += roundup(tpd_ring[i].size, 8); + } + /* init RFD ring */ + for (i = 0; i < num_rx_queues; i++) { + rfd_ring[i].dma = ring_header->dma + offset; + rfd_ring[i].desc = (u8 *) ring_header->desc + offset; + rfd_ring[i].size = sizeof(struct atl1c_rx_free_desc) * + rfd_ring[i].count; + offset += roundup(rfd_ring[i].size, 8); + } + + /* init RRD ring */ + for (i = 0; i < num_rx_queues; i++) { + rrd_ring[i].dma = ring_header->dma + offset; + rrd_ring[i].desc = (u8 *) ring_header->desc + offset; + rrd_ring[i].size = sizeof(struct atl1c_recv_ret_status) * + rrd_ring[i].count; + offset += roundup(rrd_ring[i].size, 8); + } + + adapter->smb.dma = ring_header->dma + offset; + adapter->smb.smb = (u8 *)ring_header->desc + offset; + return 0; + +err_nomem: + kfree(tpd_ring->buffer_info); + return -ENOMEM; +} + +static void atl1c_configure_des_ring(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct atl1c_rfd_ring *rfd_ring = (struct atl1c_rfd_ring *) + adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = (struct atl1c_rrd_ring *) + adapter->rrd_ring; + struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) + adapter->tpd_ring; + struct atl1c_cmb *cmb = (struct atl1c_cmb *) &adapter->cmb; + struct atl1c_smb *smb = (struct atl1c_smb *) &adapter->smb; + int i; + + /* TPD */ + AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI, + (u32)((tpd_ring[atl1c_trans_normal].dma & + AT_DMA_HI_ADDR_MASK) >> 32)); + /* just enable normal priority TX queue */ + AT_WRITE_REG(hw, REG_NTPD_HEAD_ADDR_LO, + (u32)(tpd_ring[atl1c_trans_normal].dma & + AT_DMA_LO_ADDR_MASK)); + AT_WRITE_REG(hw, REG_HTPD_HEAD_ADDR_LO, + (u32)(tpd_ring[atl1c_trans_high].dma & + AT_DMA_LO_ADDR_MASK)); + AT_WRITE_REG(hw, REG_TPD_RING_SIZE, + (u32)(tpd_ring[0].count & TPD_RING_SIZE_MASK)); + + + /* RFD */ + AT_WRITE_REG(hw, REG_RX_BASE_ADDR_HI, + (u32)((rfd_ring[0].dma & AT_DMA_HI_ADDR_MASK) >> 32)); + for (i = 0; i < adapter->num_rx_queues; i++) + AT_WRITE_REG(hw, atl1c_rfd_addr_lo_regs[i], + (u32)(rfd_ring[i].dma & AT_DMA_LO_ADDR_MASK)); + + AT_WRITE_REG(hw, REG_RFD_RING_SIZE, + rfd_ring[0].count & RFD_RING_SIZE_MASK); + AT_WRITE_REG(hw, REG_RX_BUF_SIZE, + adapter->rx_buffer_len & RX_BUF_SIZE_MASK); + + /* RRD */ + for (i = 0; i < adapter->num_rx_queues; i++) + AT_WRITE_REG(hw, atl1c_rrd_addr_lo_regs[i], + (u32)(rrd_ring[i].dma & AT_DMA_LO_ADDR_MASK)); + AT_WRITE_REG(hw, REG_RRD_RING_SIZE, + (rrd_ring[0].count & RRD_RING_SIZE_MASK)); + + /* CMB */ + AT_WRITE_REG(hw, REG_CMB_BASE_ADDR_LO, cmb->dma & AT_DMA_LO_ADDR_MASK); + + /* SMB */ + AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_HI, + (u32)((smb->dma & AT_DMA_HI_ADDR_MASK) >> 32)); + AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_LO, + (u32)(smb->dma & AT_DMA_LO_ADDR_MASK)); + /* Load all of base address above */ + AT_WRITE_REG(hw, REG_LOAD_PTR, 1); +} + +static void atl1c_configure_tx(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 dev_ctrl_data; + u32 max_pay_load; + u16 tx_offload_thresh; + u32 txq_ctrl_data; + u32 extra_size = 0; /* Jumbo frame threshold in QWORD unit */ + + extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; + tx_offload_thresh = MAX_TX_OFFLOAD_THRESH; + AT_WRITE_REG(hw, REG_TX_TSO_OFFLOAD_THRESH, + (tx_offload_thresh >> 3) & TX_TSO_OFFLOAD_THRESH_MASK); + AT_READ_REG(hw, REG_DEVICE_CTRL, &dev_ctrl_data); + max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT) & + DEVICE_CTRL_MAX_PAYLOAD_MASK; + hw->dmaw_block = min(max_pay_load, hw->dmaw_block); + max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT) & + DEVICE_CTRL_MAX_RREQ_SZ_MASK; + hw->dmar_block = min(max_pay_load, hw->dmar_block); + + txq_ctrl_data = (hw->tpd_burst & TXQ_NUM_TPD_BURST_MASK) << + TXQ_NUM_TPD_BURST_SHIFT; + if (hw->ctrl_flags & ATL1C_TXQ_MODE_ENHANCE) + txq_ctrl_data |= TXQ_CTRL_ENH_MODE; + txq_ctrl_data |= (atl1c_pay_load_size[hw->dmar_block] & + TXQ_TXF_BURST_NUM_MASK) << TXQ_TXF_BURST_NUM_SHIFT; + + AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data); +} + +static void atl1c_configure_rx(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 rxq_ctrl_data; + + rxq_ctrl_data = (hw->rfd_burst & RXQ_RFD_BURST_NUM_MASK) << + RXQ_RFD_BURST_NUM_SHIFT; + + if (hw->ctrl_flags & ATL1C_RX_IPV6_CHKSUM) + rxq_ctrl_data |= IPV6_CHKSUM_CTRL_EN; + if (hw->rss_type == atl1c_rss_ipv4) + rxq_ctrl_data |= RSS_HASH_IPV4; + if (hw->rss_type == atl1c_rss_ipv4_tcp) + rxq_ctrl_data |= RSS_HASH_IPV4_TCP; + if (hw->rss_type == atl1c_rss_ipv6) + rxq_ctrl_data |= RSS_HASH_IPV6; + if (hw->rss_type == atl1c_rss_ipv6_tcp) + rxq_ctrl_data |= RSS_HASH_IPV6_TCP; + if (hw->rss_type != atl1c_rss_disable) + rxq_ctrl_data |= RRS_HASH_CTRL_EN; + + rxq_ctrl_data |= (hw->rss_mode & RSS_MODE_MASK) << + RSS_MODE_SHIFT; + rxq_ctrl_data |= (hw->rss_hash_bits & RSS_HASH_BITS_MASK) << + RSS_HASH_BITS_SHIFT; + if (hw->ctrl_flags & ATL1C_ASPM_CTRL_MON) + rxq_ctrl_data |= (ASPM_THRUPUT_LIMIT_100M & + ASPM_THRUPUT_LIMIT_MASK) << ASPM_THRUPUT_LIMIT_SHIFT; + + AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); +} + +static void atl1c_configure_rss(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + + AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab); + AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu); +} + +static void atl1c_configure_dma(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 dma_ctrl_data; + + dma_ctrl_data = DMA_CTRL_DMAR_REQ_PRI; + if (hw->ctrl_flags & ATL1C_CMB_ENABLE) + dma_ctrl_data |= DMA_CTRL_CMB_EN; + if (hw->ctrl_flags & ATL1C_SMB_ENABLE) + dma_ctrl_data |= DMA_CTRL_SMB_EN; + else + dma_ctrl_data |= MAC_CTRL_SMB_DIS; + + switch (hw->dma_order) { + case atl1c_dma_ord_in: + dma_ctrl_data |= DMA_CTRL_DMAR_IN_ORDER; + break; + case atl1c_dma_ord_enh: + dma_ctrl_data |= DMA_CTRL_DMAR_ENH_ORDER; + break; + case atl1c_dma_ord_out: + dma_ctrl_data |= DMA_CTRL_DMAR_OUT_ORDER; + break; + default: + break; + } + + dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) + << DMA_CTRL_DMAR_BURST_LEN_SHIFT; + dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) + << DMA_CTRL_DMAW_BURST_LEN_SHIFT; + dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK) + << DMA_CTRL_DMAR_DLY_CNT_SHIFT; + dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK) + << DMA_CTRL_DMAW_DLY_CNT_SHIFT; + + AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data); +} + +/* + * Stop the mac, transmit and receive units + * hw - Struct containing variables accessed by shared code + * return : 0 or idle status (if error) + */ +static int atl1c_stop_mac(struct atl1c_hw *hw) +{ + u32 data; + int timeout; + + AT_READ_REG(hw, REG_RXQ_CTRL, &data); + data &= ~(RXQ1_CTRL_EN | RXQ2_CTRL_EN | + RXQ3_CTRL_EN | RXQ_CTRL_EN); + AT_WRITE_REG(hw, REG_RXQ_CTRL, data); + + AT_READ_REG(hw, REG_TXQ_CTRL, &data); + data &= ~TXQ_CTRL_EN; + AT_WRITE_REG(hw, REG_TWSI_CTRL, data); + + for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { + AT_READ_REG(hw, REG_IDLE_STATUS, &data); + if ((data & (IDLE_STATUS_RXQ_NO_IDLE | + IDLE_STATUS_TXQ_NO_IDLE)) == 0) + break; + msleep(1); + } + + AT_READ_REG(hw, REG_MAC_CTRL, &data); + data &= ~(MAC_CTRL_TX_EN | MAC_CTRL_RX_EN); + AT_WRITE_REG(hw, REG_MAC_CTRL, data); + + for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { + AT_READ_REG(hw, REG_IDLE_STATUS, &data); + if ((data & IDLE_STATUS_MASK) == 0) + return 0; + msleep(1); + } + return data; +} + +static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw) +{ + u32 data; + + AT_READ_REG(hw, REG_RXQ_CTRL, &data); + switch (hw->adapter->num_rx_queues) { + case 4: + data |= (RXQ3_CTRL_EN | RXQ2_CTRL_EN | RXQ1_CTRL_EN); + break; + case 3: + data |= (RXQ2_CTRL_EN | RXQ1_CTRL_EN); + break; + case 2: + data |= RXQ1_CTRL_EN; + break; + default: + break; + } + data |= RXQ_CTRL_EN; + AT_WRITE_REG(hw, REG_RXQ_CTRL, data); +} + +static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw) +{ + u32 data; + + AT_READ_REG(hw, REG_TXQ_CTRL, &data); + data |= TXQ_CTRL_EN; + AT_WRITE_REG(hw, REG_TXQ_CTRL, data); +} + +/* + * Reset the transmit and receive units; mask and clear all interrupts. + * hw - Struct containing variables accessed by shared code + * return : 0 or idle status (if error) + */ +static int atl1c_reset_mac(struct atl1c_hw *hw) +{ + struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct pci_dev *pdev = adapter->pdev; + u32 idle_status_data = 0; + int timeout = 0; + int ret; + + AT_WRITE_REG(hw, REG_IMR, 0); + AT_WRITE_REG(hw, REG_ISR, ISR_DIS_INT); + + ret = atl1c_stop_mac(hw); + if (ret) + return ret; + /* + * Issue Soft Reset to the MAC. This will reset the chip's + * transmit, receive, DMA. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + AT_WRITE_REGW(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST); + AT_WRITE_FLUSH(hw); + msleep(10); + /* Wait at least 10ms for All module to be Idle */ + for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { + AT_READ_REG(hw, REG_IDLE_STATUS, &idle_status_data); + if ((idle_status_data & IDLE_STATUS_MASK) == 0) + break; + msleep(1); + } + if (timeout >= AT_HW_MAX_IDLE_DELAY) { + dev_err(&pdev->dev, + "MAC state machine cann't be idle since" + " disabled for 10ms second\n"); + return -1; + } + return 0; +} + +static void atl1c_disable_l0s_l1(struct atl1c_hw *hw) +{ + u32 pm_ctrl_data; + + AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data); + pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK << + PM_CTRL_L1_ENTRY_TIMER_SHIFT); + pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1; + pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; + pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; + pm_ctrl_data &= ~PM_CTRL_MAC_ASPM_CHK; + pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1; + + pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN; + pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN; + pm_ctrl_data |= PM_CTRL_SERDES_L1_EN; + AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data); +} + +/* + * Set ASPM state. + * Enable/disable L0s/L1 depend on link state. + */ +static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup) +{ + u32 pm_ctrl_data; + + AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data); + + pm_ctrl_data &= PM_CTRL_SERDES_PD_EX_L1; + pm_ctrl_data |= ~PM_CTRL_SERDES_BUDS_RX_L1_EN; + pm_ctrl_data |= ~PM_CTRL_SERDES_L1_EN; + pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK << + PM_CTRL_L1_ENTRY_TIMER_SHIFT); + + pm_ctrl_data |= PM_CTRL_MAC_ASPM_CHK; + + if (linkup) { + pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN; + pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1; + + if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) { + pm_ctrl_data |= AT_ASPM_L1_TIMER << + PM_CTRL_L1_ENTRY_TIMER_SHIFT; + pm_ctrl_data |= PM_CTRL_ASPM_L1_EN; + } else + pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; + + if (hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT) + pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN; + else + pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; + + } else { + pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; + pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN; + + pm_ctrl_data |= PM_CTRL_CLK_SWH_L1; + + if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) + pm_ctrl_data |= PM_CTRL_ASPM_L1_EN; + else + pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; + } + + AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data); +} + +static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + u32 mac_ctrl_data; + + mac_ctrl_data = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN; + mac_ctrl_data |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); + + if (adapter->link_duplex == FULL_DUPLEX) { + hw->mac_duplex = true; + mac_ctrl_data |= MAC_CTRL_DUPLX; + } + + if (adapter->link_speed == SPEED_1000) + hw->mac_speed = atl1c_mac_speed_1000; + else + hw->mac_speed = atl1c_mac_speed_10_100; + + mac_ctrl_data |= (hw->mac_speed & MAC_CTRL_SPEED_MASK) << + MAC_CTRL_SPEED_SHIFT; + + mac_ctrl_data |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + mac_ctrl_data |= ((hw->preamble_len & MAC_CTRL_PRMLEN_MASK) << + MAC_CTRL_PRMLEN_SHIFT); + + if (adapter->vlgrp) + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + + mac_ctrl_data |= MAC_CTRL_BC_EN; + if (netdev->flags & IFF_PROMISC) + mac_ctrl_data |= MAC_CTRL_PROMIS_EN; + if (netdev->flags & IFF_ALLMULTI) + mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; + + mac_ctrl_data |= MAC_CTRL_SINGLE_PAUSE_EN; + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); +} + +/* + * atl1c_configure - Configure Transmit&Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Tx /Rx unit of the MAC after a reset. + */ +static int atl1c_configure(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 master_ctrl_data = 0; + u32 intr_modrt_data; + + /* clear interrupt status */ + AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF); + /* Clear any WOL status */ + AT_WRITE_REG(hw, REG_WOL_CTRL, 0); + /* set Interrupt Clear Timer + * HW will enable self to assert interrupt event to system after + * waiting x-time for software to notify it accept interrupt. + */ + AT_WRITE_REG(hw, REG_INT_RETRIG_TIMER, + hw->ict & INT_RETRIG_TIMER_MASK); + + atl1c_configure_des_ring(adapter); + + if (hw->ctrl_flags & ATL1C_INTR_MODRT_ENABLE) { + intr_modrt_data = (hw->tx_imt & IRQ_MODRT_TIMER_MASK) << + IRQ_MODRT_TX_TIMER_SHIFT; + intr_modrt_data |= (hw->rx_imt & IRQ_MODRT_TIMER_MASK) << + IRQ_MODRT_RX_TIMER_SHIFT; + AT_WRITE_REG(hw, REG_IRQ_MODRT_TIMER_INIT, intr_modrt_data); + master_ctrl_data |= + MASTER_CTRL_TX_ITIMER_EN | MASTER_CTRL_RX_ITIMER_EN; + } + + if (hw->ctrl_flags & ATL1C_INTR_CLEAR_ON_READ) + master_ctrl_data |= MASTER_CTRL_INT_RDCLR; + + AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); + + if (hw->ctrl_flags & ATL1C_CMB_ENABLE) { + AT_WRITE_REG(hw, REG_CMB_TPD_THRESH, + hw->cmb_tpd & CMB_TPD_THRESH_MASK); + AT_WRITE_REG(hw, REG_CMB_TX_TIMER, + hw->cmb_tx_timer & CMB_TX_TIMER_MASK); + } + + if (hw->ctrl_flags & ATL1C_SMB_ENABLE) + AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, + hw->smb_timer & SMB_STAT_TIMER_MASK); + /* set MTU */ + AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN + + VLAN_HLEN + ETH_FCS_LEN); + /* HDS, disable */ + AT_WRITE_REG(hw, REG_HDS_CTRL, 0); + + atl1c_configure_tx(adapter); + atl1c_configure_rx(adapter); + atl1c_configure_rss(adapter); + atl1c_configure_dma(adapter); + + return 0; +} + +static void atl1c_update_hw_stats(struct atl1c_adapter *adapter) +{ + u16 hw_reg_addr = 0; + unsigned long *stats_item = NULL; + u32 data; + + /* update rx status */ + hw_reg_addr = REG_MAC_RX_STATUS_BIN; + stats_item = &adapter->hw_stats.rx_ok; + while (hw_reg_addr <= REG_MAC_RX_STATUS_END) { + AT_READ_REG(&adapter->hw, hw_reg_addr, &data); + *stats_item += data; + stats_item++; + hw_reg_addr += 4; + } +/* update tx status */ + hw_reg_addr = REG_MAC_TX_STATUS_BIN; + stats_item = &adapter->hw_stats.tx_ok; + while (hw_reg_addr <= REG_MAC_TX_STATUS_END) { + AT_READ_REG(&adapter->hw, hw_reg_addr, &data); + *stats_item += data; + stats_item++; + hw_reg_addr += 4; + } +} + +/* + * atl1c_get_stats - Get System Network Statistics + * @netdev: network interface device structure + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + */ +static struct net_device_stats *atl1c_get_stats(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw_stats *hw_stats = &adapter->hw_stats; + struct net_device_stats *net_stats = &adapter->net_stats; + + atl1c_update_hw_stats(adapter); + net_stats->rx_packets = hw_stats->rx_ok; + net_stats->tx_packets = hw_stats->tx_ok; + net_stats->rx_bytes = hw_stats->rx_byte_cnt; + net_stats->tx_bytes = hw_stats->tx_byte_cnt; + net_stats->multicast = hw_stats->rx_mcast; + net_stats->collisions = hw_stats->tx_1_col + + hw_stats->tx_2_col * 2 + + hw_stats->tx_late_col + hw_stats->tx_abort_col; + net_stats->rx_errors = hw_stats->rx_frag + hw_stats->rx_fcs_err + + hw_stats->rx_len_err + hw_stats->rx_sz_ov + + hw_stats->rx_rrd_ov + hw_stats->rx_align_err; + net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov; + net_stats->rx_length_errors = hw_stats->rx_len_err; + net_stats->rx_crc_errors = hw_stats->rx_fcs_err; + net_stats->rx_frame_errors = hw_stats->rx_align_err; + net_stats->rx_over_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; + + net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; + + net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col + + hw_stats->tx_underrun + hw_stats->tx_trunc; + net_stats->tx_fifo_errors = hw_stats->tx_underrun; + net_stats->tx_aborted_errors = hw_stats->tx_abort_col; + net_stats->tx_window_errors = hw_stats->tx_late_col; + + return &adapter->net_stats; +} + +static inline void atl1c_clear_phy_int(struct atl1c_adapter *adapter) +{ + u16 phy_data; + + spin_lock(&adapter->mdio_lock); + atl1c_read_phy_reg(&adapter->hw, MII_ISR, &phy_data); + spin_unlock(&adapter->mdio_lock); +} + +static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) + &adapter->tpd_ring[type]; + struct atl1c_buffer *buffer_info; + u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); + u16 hw_next_to_clean; + u16 shift; + u32 data; + + if (type == atl1c_trans_high) + shift = MB_HTPD_CONS_IDX_SHIFT; + else + shift = MB_NTPD_CONS_IDX_SHIFT; + + AT_READ_REG(&adapter->hw, REG_MB_PRIO_CONS_IDX, &data); + hw_next_to_clean = (data >> shift) & MB_PRIO_PROD_IDX_MASK; + + while (next_to_clean != hw_next_to_clean) { + buffer_info = &tpd_ring->buffer_info[next_to_clean]; + if (buffer_info->state == ATL1_BUFFER_BUSY) { + pci_unmap_page(adapter->pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->dma = 0; + if (buffer_info->skb) { + dev_kfree_skb_irq(buffer_info->skb); + buffer_info->skb = NULL; + } + buffer_info->state = ATL1_BUFFER_FREE; + } + if (++next_to_clean == tpd_ring->count) + next_to_clean = 0; + atomic_set(&tpd_ring->next_to_clean, next_to_clean); + } + + if (netif_queue_stopped(adapter->netdev) && + netif_carrier_ok(adapter->netdev)) { + netif_wake_queue(adapter->netdev); + } + + return true; +} + +/* + * atl1c_intr - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + * @pt_regs: CPU registers structure + */ +static irqreturn_t atl1c_intr(int irq, void *data) +{ + struct net_device *netdev = data; + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + struct atl1c_hw *hw = &adapter->hw; + int max_ints = AT_MAX_INT_WORK; + int handled = IRQ_NONE; + u32 status; + u32 reg_data; + + do { + AT_READ_REG(hw, REG_ISR, ®_data); + status = reg_data & hw->intr_mask; + + if (status == 0 || (status & ISR_DIS_INT) != 0) { + if (max_ints != AT_MAX_INT_WORK) + handled = IRQ_HANDLED; + break; + } + /* link event */ + if (status & ISR_GPHY) + atl1c_clear_phy_int(adapter); + /* Ack ISR */ + AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); + if (status & ISR_RX_PKT) { + if (likely(napi_schedule_prep(&adapter->napi))) { + hw->intr_mask &= ~ISR_RX_PKT; + AT_WRITE_REG(hw, REG_IMR, hw->intr_mask); + __napi_schedule(&adapter->napi); + } + } + if (status & ISR_TX_PKT) + atl1c_clean_tx_irq(adapter, atl1c_trans_normal); + + handled = IRQ_HANDLED; + /* check if PCIE PHY Link down */ + if (status & ISR_ERROR) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "atl1c hardware error (status = 0x%x)\n", + status & ISR_ERROR); + /* reset MAC */ + hw->intr_mask &= ~ISR_ERROR; + AT_WRITE_REG(hw, REG_IMR, hw->intr_mask); + schedule_work(&adapter->reset_task); + break; + } + + if (status & ISR_OVER) + if (netif_msg_intr(adapter)) + dev_warn(&pdev->dev, + "TX/RX over flow (status = 0x%x)\n", + status & ISR_OVER); + + /* link event */ + if (status & (ISR_GPHY | ISR_MANUAL)) { + adapter->net_stats.tx_carrier_errors++; + atl1c_link_chg_event(adapter); + break; + } + + } while (--max_ints > 0); + /* re-enable Interrupt*/ + AT_WRITE_REG(&adapter->hw, REG_ISR, 0); + return handled; +} + +static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter, + struct sk_buff *skb, struct atl1c_recv_ret_status *prrs) +{ + /* + * The pid field in RRS in not correct sometimes, so we + * cannot figure out if the packet is fragmented or not, + * so we tell the KERNEL CHECKSUM_NONE + */ + skb->ip_summed = CHECKSUM_NONE; +} + +static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid) +{ + struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[ringid]; + struct pci_dev *pdev = adapter->pdev; + struct atl1c_buffer *buffer_info, *next_info; + struct sk_buff *skb; + void *vir_addr = NULL; + u16 num_alloc = 0; + u16 rfd_next_to_use, next_next; + struct atl1c_rx_free_desc *rfd_desc; + + next_next = rfd_next_to_use = rfd_ring->next_to_use; + if (++next_next == rfd_ring->count) + next_next = 0; + buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; + next_info = &rfd_ring->buffer_info[next_next]; + + while (next_info->state == ATL1_BUFFER_FREE) { + rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use); + + skb = dev_alloc_skb(adapter->rx_buffer_len); + if (unlikely(!skb)) { + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, "alloc rx buffer failed\n"); + break; + } + + /* + * Make buffer alignment 2 beyond a 16 byte boundary + * this will result in a 16 byte aligned IP header after + * the 14 byte MAC header is removed + */ + vir_addr = skb->data; + buffer_info->state = ATL1_BUFFER_BUSY; + buffer_info->skb = skb; + buffer_info->length = adapter->rx_buffer_len; + buffer_info->dma = pci_map_single(pdev, vir_addr, + buffer_info->length, + PCI_DMA_FROMDEVICE); + rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); + rfd_next_to_use = next_next; + if (++next_next == rfd_ring->count) + next_next = 0; + buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; + next_info = &rfd_ring->buffer_info[next_next]; + num_alloc++; + } + + if (num_alloc) { + /* TODO: update mailbox here */ + wmb(); + rfd_ring->next_to_use = rfd_next_to_use; + AT_WRITE_REG(&adapter->hw, atl1c_rfd_prod_idx_regs[ringid], + rfd_ring->next_to_use & MB_RFDX_PROD_IDX_MASK); + } + + return num_alloc; +} + +static void atl1c_clean_rrd(struct atl1c_rrd_ring *rrd_ring, + struct atl1c_recv_ret_status *rrs, u16 num) +{ + u16 i; + /* the relationship between rrd and rfd is one map one */ + for (i = 0; i < num; i++, rrs = ATL1C_RRD_DESC(rrd_ring, + rrd_ring->next_to_clean)) { + rrs->word3 &= ~RRS_RXD_UPDATED; + if (++rrd_ring->next_to_clean == rrd_ring->count) + rrd_ring->next_to_clean = 0; + } +} + +static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring, + struct atl1c_recv_ret_status *rrs, u16 num) +{ + u16 i; + u16 rfd_index; + struct atl1c_buffer *buffer_info = rfd_ring->buffer_info; + + rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) & + RRS_RX_RFD_INDEX_MASK; + for (i = 0; i < num; i++) { + buffer_info[rfd_index].skb = NULL; + buffer_info[rfd_index].state = ATL1_BUFFER_FREE; + if (++rfd_index == rfd_ring->count) + rfd_index = 0; + } + rfd_ring->next_to_clean = rfd_index; +} + +static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, + int *work_done, int work_to_do) +{ + u16 rfd_num, rfd_index; + u16 count = 0; + u16 length; + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[que]; + struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring[que]; + struct sk_buff *skb; + struct atl1c_recv_ret_status *rrs; + struct atl1c_buffer *buffer_info; + + while (1) { + if (*work_done >= work_to_do) + break; + rrs = ATL1C_RRD_DESC(rrd_ring, rrd_ring->next_to_clean); + if (likely(RRS_RXD_IS_VALID(rrs->word3))) { + rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) & + RRS_RX_RFD_CNT_MASK; + if (unlikely(rfd_num) != 1) + /* TODO support mul rfd*/ + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, + "Multi rfd not support yet!\n"); + goto rrs_checked; + } else { + break; + } +rrs_checked: + atl1c_clean_rrd(rrd_ring, rrs, rfd_num); + if (rrs->word3 & (RRS_RX_ERR_SUM | RRS_802_3_LEN_ERR)) { + atl1c_clean_rfd(rfd_ring, rrs, rfd_num); + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, + "wrong packet! rrs word3 is %x\n", + rrs->word3); + continue; + } + + length = le16_to_cpu((rrs->word3 >> RRS_PKT_SIZE_SHIFT) & + RRS_PKT_SIZE_MASK); + /* Good Receive */ + if (likely(rfd_num == 1)) { + rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) & + RRS_RX_RFD_INDEX_MASK; + buffer_info = &rfd_ring->buffer_info[rfd_index]; + pci_unmap_single(pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_FROMDEVICE); + skb = buffer_info->skb; + } else { + /* TODO */ + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, + "Multi rfd not support yet!\n"); + break; + } + atl1c_clean_rfd(rfd_ring, rrs, rfd_num); + skb_put(skb, length - ETH_FCS_LEN); + skb->protocol = eth_type_trans(skb, netdev); + skb->dev = netdev; + atl1c_rx_checksum(adapter, skb, rrs); + if (unlikely(adapter->vlgrp) && rrs->word3 & RRS_VLAN_INS) { + u16 vlan; + + AT_TAG_TO_VLAN(rrs->vlan_tag, vlan); + vlan = le16_to_cpu(vlan); + vlan_hwaccel_receive_skb(skb, adapter->vlgrp, vlan); + } else + netif_receive_skb(skb); + + netdev->last_rx = jiffies; + (*work_done)++; + count++; + } + if (count) + atl1c_alloc_rx_buffer(adapter, que); +} + +/* + * atl1c_clean - NAPI Rx polling callback + * @adapter: board private structure + */ +static int atl1c_clean(struct napi_struct *napi, int budget) +{ + struct atl1c_adapter *adapter = + container_of(napi, struct atl1c_adapter, napi); + int work_done = 0; + + /* Keep link state information with original netdev */ + if (!netif_carrier_ok(adapter->netdev)) + goto quit_polling; + /* just enable one RXQ */ + atl1c_clean_rx_irq(adapter, 0, &work_done, budget); + + if (work_done < budget) { +quit_polling: + napi_complete(napi); + adapter->hw.intr_mask |= ISR_RX_PKT; + AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask); + } + return work_done; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER + +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ +static void atl1c_netpoll(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + disable_irq(adapter->pdev->irq); + atl1c_intr(adapter->pdev->irq, netdev); + enable_irq(adapter->pdev->irq); +} +#endif + +static inline u16 atl1c_tpd_avail(struct atl1c_adapter *adapter, enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + u16 next_to_use = 0; + u16 next_to_clean = 0; + + next_to_clean = atomic_read(&tpd_ring->next_to_clean); + next_to_use = tpd_ring->next_to_use; + + return (u16)(next_to_clean > next_to_use) ? + (next_to_clean - next_to_use - 1) : + (tpd_ring->count + next_to_clean - next_to_use - 1); +} + +/* + * get next usable tpd + * Note: should call atl1c_tdp_avail to make sure + * there is enough tpd to use + */ +static struct atl1c_tpd_desc *atl1c_get_tpd(struct atl1c_adapter *adapter, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + struct atl1c_tpd_desc *tpd_desc; + u16 next_to_use = 0; + + next_to_use = tpd_ring->next_to_use; + if (++tpd_ring->next_to_use == tpd_ring->count) + tpd_ring->next_to_use = 0; + tpd_desc = ATL1C_TPD_DESC(tpd_ring, next_to_use); + memset(tpd_desc, 0, sizeof(struct atl1c_tpd_desc)); + return tpd_desc; +} + +static struct atl1c_buffer * +atl1c_get_tx_buffer(struct atl1c_adapter *adapter, struct atl1c_tpd_desc *tpd) +{ + struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; + + return &tpd_ring->buffer_info[tpd - + (struct atl1c_tpd_desc *)tpd_ring->desc]; +} + +/* Calculate the transmit packet descript needed*/ +static u16 atl1c_cal_tpd_req(const struct sk_buff *skb) +{ + u16 tpd_req; + u16 proto_hdr_len = 0; + + tpd_req = skb_shinfo(skb)->nr_frags + 1; + + if (skb_is_gso(skb)) { + proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + if (proto_hdr_len < skb_headlen(skb)) + tpd_req++; + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + tpd_req++; + } + return tpd_req; +} + +static int atl1c_tso_csum(struct atl1c_adapter *adapter, + struct sk_buff *skb, + struct atl1c_tpd_desc **tpd, + enum atl1c_trans_queue type) +{ + struct pci_dev *pdev = adapter->pdev; + u8 hdr_len; + u32 real_len; + unsigned short offload_type; + int err; + + if (skb_is_gso(skb)) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (unlikely(err)) + return -1; + } + offload_type = skb_shinfo(skb)->gso_type; + + if (offload_type & SKB_GSO_TCPV4) { + real_len = (((unsigned char *)ip_hdr(skb) - skb->data) + + ntohs(ip_hdr(skb)->tot_len)); + + if (real_len < skb->len) + pskb_trim(skb, real_len); + + hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (unlikely(skb->len == hdr_len)) { + /* only xsum need */ + if (netif_msg_tx_queued(adapter)) + dev_warn(&pdev->dev, + "IPV4 tso with zero data??\n"); + goto check_sum; + } else { + ip_hdr(skb)->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic( + ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + (*tpd)->word1 |= 1 << TPD_IPV4_PACKET_SHIFT; + } + } + + if (offload_type & SKB_GSO_TCPV6) { + struct atl1c_tpd_ext_desc *etpd = + *(struct atl1c_tpd_ext_desc **)(tpd); + + memset(etpd, 0, sizeof(struct atl1c_tpd_ext_desc)); + *tpd = atl1c_get_tpd(adapter, type); + ipv6_hdr(skb)->payload_len = 0; + /* check payload == 0 byte ? */ + hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (unlikely(skb->len == hdr_len)) { + /* only xsum need */ + if (netif_msg_tx_queued(adapter)) + dev_warn(&pdev->dev, + "IPV6 tso with zero data??\n"); + goto check_sum; + } else + tcp_hdr(skb)->check = ~csum_ipv6_magic( + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + etpd->word1 |= 1 << TPD_LSO_EN_SHIFT; + etpd->word1 |= 1 << TPD_LSO_VER_SHIFT; + etpd->pkt_len = cpu_to_le32(skb->len); + (*tpd)->word1 |= 1 << TPD_LSO_VER_SHIFT; + } + + (*tpd)->word1 |= 1 << TPD_LSO_EN_SHIFT; + (*tpd)->word1 |= (skb_transport_offset(skb) & TPD_TCPHDR_OFFSET_MASK) << + TPD_TCPHDR_OFFSET_SHIFT; + (*tpd)->word1 |= (skb_shinfo(skb)->gso_size & TPD_MSS_MASK) << + TPD_MSS_SHIFT; + return 0; + } + +check_sum: + if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + u8 css, cso; + cso = skb_transport_offset(skb); + + if (unlikely(cso & 0x1)) { + if (netif_msg_tx_err(adapter)) + dev_err(&adapter->pdev->dev, + "payload offset should not an event number\n"); + return -1; + } else { + css = cso + skb->csum_offset; + + (*tpd)->word1 |= ((cso >> 1) & TPD_PLOADOFFSET_MASK) << + TPD_PLOADOFFSET_SHIFT; + (*tpd)->word1 |= ((css >> 1) & TPD_CCSUM_OFFSET_MASK) << + TPD_CCSUM_OFFSET_SHIFT; + (*tpd)->word1 |= 1 << TPD_CCSUM_EN_SHIFT; + } + } + return 0; +} + +static void atl1c_tx_map(struct atl1c_adapter *adapter, + struct sk_buff *skb, struct atl1c_tpd_desc *tpd, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_desc *use_tpd = NULL; + struct atl1c_buffer *buffer_info = NULL; + u16 buf_len = skb_headlen(skb); + u16 map_len = 0; + u16 mapped_len = 0; + u16 hdr_len = 0; + u16 nr_frags; + u16 f; + int tso; + + nr_frags = skb_shinfo(skb)->nr_frags; + tso = (tpd->word1 >> TPD_LSO_EN_SHIFT) & TPD_LSO_EN_MASK; + if (tso) { + /* TSO */ + map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + use_tpd = tpd; + + buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); + buffer_info->length = map_len; + buffer_info->dma = pci_map_single(adapter->pdev, + skb->data, hdr_len, PCI_DMA_TODEVICE); + buffer_info->state = ATL1_BUFFER_BUSY; + mapped_len += map_len; + use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); + use_tpd->buffer_len = cpu_to_le16(buffer_info->length); + } + + if (mapped_len < buf_len) { + /* mapped_len == 0, means we should use the first tpd, + which is given by caller */ + if (mapped_len == 0) + use_tpd = tpd; + else { + use_tpd = atl1c_get_tpd(adapter, type); + memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); + use_tpd = atl1c_get_tpd(adapter, type); + memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); + } + buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); + buffer_info->length = buf_len - mapped_len; + buffer_info->dma = + pci_map_single(adapter->pdev, skb->data + mapped_len, + buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->state = ATL1_BUFFER_BUSY; + + use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); + use_tpd->buffer_len = cpu_to_le16(buffer_info->length); + } + + for (f = 0; f < nr_frags; f++) { + struct skb_frag_struct *frag; + + frag = &skb_shinfo(skb)->frags[f]; + + use_tpd = atl1c_get_tpd(adapter, type); + memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); + + buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); + buffer_info->length = frag->size; + buffer_info->dma = + pci_map_page(adapter->pdev, frag->page, + frag->page_offset, + buffer_info->length, + PCI_DMA_TODEVICE); + buffer_info->state = ATL1_BUFFER_BUSY; + + use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); + use_tpd->buffer_len = cpu_to_le16(buffer_info->length); + } + + /* The last tpd */ + use_tpd->word1 |= 1 << TPD_EOP_SHIFT; + /* The last buffer info contain the skb address, + so it will be free after unmap */ + buffer_info->skb = skb; +} + +static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb, + struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + u32 prod_data; + + AT_READ_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, &prod_data); + switch (type) { + case atl1c_trans_high: + prod_data &= 0xFFFF0000; + prod_data |= tpd_ring->next_to_use & 0xFFFF; + break; + case atl1c_trans_normal: + prod_data &= 0x0000FFFF; + prod_data |= (tpd_ring->next_to_use & 0xFFFF) << 16; + break; + default: + break; + } + wmb(); + AT_WRITE_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, prod_data); +} + +static int atl1c_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + u16 tpd_req = 1; + struct atl1c_tpd_desc *tpd; + enum atl1c_trans_queue type = atl1c_trans_normal; + + if (test_bit(__AT_DOWN, &adapter->flags)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + tpd_req = atl1c_cal_tpd_req(skb); + if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) { + if (netif_msg_pktdata(adapter)) + dev_info(&adapter->pdev->dev, "tx locked\n"); + return NETDEV_TX_LOCKED; + } + if (skb->mark == 0x01) + type = atl1c_trans_high; + else + type = atl1c_trans_normal; + + if (atl1c_tpd_avail(adapter, type) < tpd_req) { + /* no enough descriptor, just stop queue */ + netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + tpd = atl1c_get_tpd(adapter, type); + + /* do TSO and check sum */ + if (atl1c_tso_csum(adapter, skb, &tpd, type) != 0) { + spin_unlock_irqrestore(&adapter->tx_lock, flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (unlikely(adapter->vlgrp && vlan_tx_tag_present(skb))) { + u16 vlan = vlan_tx_tag_get(skb); + __le16 tag; + + vlan = cpu_to_le16(vlan); + AT_VLAN_TO_TAG(vlan, tag); + tpd->word1 |= 1 << TPD_INS_VTAG_SHIFT; + tpd->vlan_tag = tag; + } + + if (skb_network_offset(skb) != ETH_HLEN) + tpd->word1 |= 1 << TPD_ETH_TYPE_SHIFT; /* Ethernet frame */ + + atl1c_tx_map(adapter, skb, tpd, type); + atl1c_tx_queue(adapter, skb, tpd, type); + + netdev->trans_start = jiffies; + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_OK; +} + +static void atl1c_free_irq(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + free_irq(adapter->pdev->irq, netdev); + + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); +} + +static int atl1c_request_irq(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + int flags = 0; + int err = 0; + + adapter->have_msi = true; + err = pci_enable_msi(adapter->pdev); + if (err) { + if (netif_msg_ifup(adapter)) + dev_err(&pdev->dev, + "Unable to allocate MSI interrupt Error: %d\n", + err); + adapter->have_msi = false; + } else + netdev->irq = pdev->irq; + + if (!adapter->have_msi) + flags |= IRQF_SHARED; + err = request_irq(adapter->pdev->irq, &atl1c_intr, flags, + netdev->name, netdev); + if (err) { + if (netif_msg_ifup(adapter)) + dev_err(&pdev->dev, + "Unable to allocate interrupt Error: %d\n", + err); + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); + return err; + } + if (netif_msg_ifup(adapter)) + dev_dbg(&pdev->dev, "atl1c_request_irq OK\n"); + return err; +} + +int atl1c_up(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int num; + int err; + int i; + + netif_carrier_off(netdev); + atl1c_init_ring_ptrs(adapter); + atl1c_set_multi(netdev); + atl1c_restore_vlan(adapter); + + for (i = 0; i < adapter->num_rx_queues; i++) { + num = atl1c_alloc_rx_buffer(adapter, i); + if (unlikely(num == 0)) { + err = -ENOMEM; + goto err_alloc_rx; + } + } + + if (atl1c_configure(adapter)) { + err = -EIO; + goto err_up; + } + + err = atl1c_request_irq(adapter); + if (unlikely(err)) + goto err_up; + + clear_bit(__AT_DOWN, &adapter->flags); + napi_enable(&adapter->napi); + atl1c_irq_enable(adapter); + atl1c_check_link_status(adapter); + netif_start_queue(netdev); + return err; + +err_up: +err_alloc_rx: + atl1c_clean_rx_ring(adapter); + return err; +} + +void atl1c_down(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + atl1c_del_timer(adapter); + atl1c_cancel_work(adapter); + + /* signal that we're down so the interrupt handler does not + * reschedule our watchdog timer */ + set_bit(__AT_DOWN, &adapter->flags); + netif_carrier_off(netdev); + napi_disable(&adapter->napi); + atl1c_irq_disable(adapter); + atl1c_free_irq(adapter); + AT_WRITE_REG(&adapter->hw, REG_ISR, ISR_DIS_INT); + /* reset MAC to disable all RX/TX */ + atl1c_reset_mac(&adapter->hw); + msleep(1); + + adapter->link_speed = SPEED_0; + adapter->link_duplex = -1; + atl1c_clean_tx_ring(adapter, atl1c_trans_normal); + atl1c_clean_tx_ring(adapter, atl1c_trans_high); + atl1c_clean_rx_ring(adapter); +} + +/* + * atl1c_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + */ +static int atl1c_open(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + int err; + + /* disallow open during test */ + if (test_bit(__AT_TESTING, &adapter->flags)) + return -EBUSY; + + /* allocate rx/tx dma buffer & descriptors */ + err = atl1c_setup_ring_resources(adapter); + if (unlikely(err)) + return err; + + err = atl1c_up(adapter); + if (unlikely(err)) + goto err_up; + + if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) { + u32 phy_data; + + AT_READ_REG(&adapter->hw, REG_MDIO_CTRL, &phy_data); + phy_data |= MDIO_AP_EN; + AT_WRITE_REG(&adapter->hw, REG_MDIO_CTRL, phy_data); + } + return 0; + +err_up: + atl1c_free_irq(adapter); + atl1c_free_ring_resources(adapter); + atl1c_reset_mac(&adapter->hw); + return err; +} + +/* + * atl1c_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + */ +static int atl1c_close(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); + atl1c_down(adapter); + atl1c_free_ring_resources(adapter); + return 0; +} + +static int atl1c_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u32 ctrl; + u32 mac_ctrl_data; + u32 master_ctrl_data; + u32 wol_ctrl_data; + u16 mii_bmsr_data; + u16 save_autoneg_advertised; + u16 mii_intr_status_data; + u32 wufc = adapter->wol; + u32 i; + int retval = 0; + + if (netif_running(netdev)) { + WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); + atl1c_down(adapter); + } + netif_device_detach(netdev); + atl1c_disable_l0s_l1(hw); + retval = pci_save_state(pdev); + if (retval) + return retval; + if (wufc) { + AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data); + master_ctrl_data &= ~MASTER_CTRL_CLK_SEL_DIS; + + /* get link status */ + atl1c_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); + atl1c_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); + save_autoneg_advertised = hw->autoneg_advertised; + hw->autoneg_advertised = ADVERTISED_10baseT_Half; + if (atl1c_restart_autoneg(hw) != 0) + if (netif_msg_link(adapter)) + dev_warn(&pdev->dev, "phy autoneg failed\n"); + hw->phy_configured = false; /* re-init PHY when resume */ + hw->autoneg_advertised = save_autoneg_advertised; + /* turn on magic packet wol */ + if (wufc & AT_WUFC_MAG) + wol_ctrl_data = WOL_MAGIC_EN | WOL_MAGIC_PME_EN; + + if (wufc & AT_WUFC_LNKC) { + for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { + msleep(100); + atl1c_read_phy_reg(hw, MII_BMSR, + (u16 *)&mii_bmsr_data); + if (mii_bmsr_data & BMSR_LSTATUS) + break; + } + if ((mii_bmsr_data & BMSR_LSTATUS) == 0) + if (netif_msg_link(adapter)) + dev_warn(&pdev->dev, + "%s: Link may change" + "when suspend\n", + atl1c_driver_name); + wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; + /* only link up can wake up */ + if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) { + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, + "%s: read write phy " + "register failed.\n", + atl1c_driver_name); + goto wol_dis; + } + } + /* clear phy interrupt */ + atl1c_read_phy_reg(hw, MII_ISR, &mii_intr_status_data); + /* Config MAC Ctrl register */ + mac_ctrl_data = MAC_CTRL_RX_EN; + /* set to 10/100M halt duplex */ + mac_ctrl_data |= atl1c_mac_speed_10_100 << MAC_CTRL_SPEED_SHIFT; + mac_ctrl_data |= (((u32)adapter->hw.preamble_len & + MAC_CTRL_PRMLEN_MASK) << + MAC_CTRL_PRMLEN_SHIFT); + + if (adapter->vlgrp) + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + + /* magic packet maybe Broadcast&multicast&Unicast frame */ + if (wufc & AT_WUFC_MAG) + mac_ctrl_data |= MAC_CTRL_BC_EN; + + if (netif_msg_hw(adapter)) + dev_dbg(&pdev->dev, + "%s: suspend MAC=0x%x\n", + atl1c_driver_name, mac_ctrl_data); + AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); + AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data); + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); + + /* pcie patch */ + AT_READ_REG(hw, REG_PCIE_PHYMISC, &ctrl); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + goto suspend_exit; + } +wol_dis: + + /* WOL disabled */ + AT_WRITE_REG(hw, REG_WOL_CTRL, 0); + + /* pcie patch */ + AT_READ_REG(hw, REG_PCIE_PHYMISC, &ctrl); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + + atl1c_phy_disable(hw); + hw->phy_configured = false; /* re-init PHY when resume */ + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); +suspend_exit: + + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int atl1c_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); + + atl1c_phy_reset(&adapter->hw); + atl1c_reset_mac(&adapter->hw); + netif_device_attach(netdev); + if (netif_running(netdev)) + atl1c_up(adapter); + + return 0; +} + +static void atl1c_shutdown(struct pci_dev *pdev) +{ + atl1c_suspend(pdev, PMSG_SUSPEND); +} + +static const struct net_device_ops atl1c_netdev_ops = { + .ndo_open = atl1c_open, + .ndo_stop = atl1c_close, + .ndo_validate_addr = eth_validate_addr, + .ndo_start_xmit = atl1c_xmit_frame, + .ndo_set_mac_address = atl1c_set_mac_addr, + .ndo_set_multicast_list = atl1c_set_multi, + .ndo_change_mtu = atl1c_change_mtu, + .ndo_do_ioctl = atl1c_ioctl, + .ndo_tx_timeout = atl1c_tx_timeout, + .ndo_get_stats = atl1c_get_stats, + .ndo_vlan_rx_register = atl1c_vlan_rx_register, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = atl1c_netpoll, +#endif +}; + +static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev) +{ + SET_NETDEV_DEV(netdev, &pdev->dev); + pci_set_drvdata(pdev, netdev); + + netdev->irq = pdev->irq; + netdev->netdev_ops = &atl1c_netdev_ops; + netdev->watchdog_timeo = AT_TX_WATCHDOG; + atl1c_set_ethtool_ops(netdev); + + /* TODO: add when ready */ + netdev->features = NETIF_F_SG | + NETIF_F_HW_CSUM | + NETIF_F_HW_VLAN_TX | + NETIF_F_HW_VLAN_RX | + NETIF_F_TSO | + NETIF_F_TSO6; + return 0; +} + +/* + * atl1c_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in atl1c_pci_tbl + * + * Returns 0 on success, negative on failure + * + * atl1c_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + */ +static int __devinit atl1c_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct atl1c_adapter *adapter; + static int cards_found; + + int err = 0; + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + err = pci_enable_device_mem(pdev); + if (err) { + dev_err(&pdev->dev, "cannot enable PCI device\n"); + return err; + } + + /* + * The atl1c chip can DMA to 64-bit addresses, but it uses a single + * shared register for the high 32 bits, so only a single, aligned, + * 4 GB physical address range can be used at a time. + * + * Supporting 64-bit DMA on this hardware is more trouble than it's + * worth. It is far easier to limit to 32-bit DMA than update + * various kernel subsystems to support the mechanics required by a + * fixed-high-32-bit system. + */ + if ((pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) || + (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK) != 0)) { + dev_err(&pdev->dev, "No usable DMA configuration,aborting\n"); + goto err_dma; + } + + err = pci_request_regions(pdev, atl1c_driver_name); + if (err) { + dev_err(&pdev->dev, "cannot obtain PCI resources\n"); + goto err_pci_reg; + } + + pci_set_master(pdev); + + netdev = alloc_etherdev(sizeof(struct atl1c_adapter)); + if (netdev == NULL) { + err = -ENOMEM; + dev_err(&pdev->dev, "etherdev alloc failed\n"); + goto err_alloc_etherdev; + } + + err = atl1c_init_netdev(netdev, pdev); + if (err) { + dev_err(&pdev->dev, "init netdevice failed\n"); + goto err_init_netdev; + } + adapter = netdev_priv(netdev); + adapter->bd_number = cards_found; + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->hw.adapter = adapter; + adapter->msg_enable = netif_msg_init(-1, atl1c_default_msg); + adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if (!adapter->hw.hw_addr) { + err = -EIO; + dev_err(&pdev->dev, "cannot map device registers\n"); + goto err_ioremap; + } + netdev->base_addr = (unsigned long)adapter->hw.hw_addr; + + /* init mii data */ + adapter->mii.dev = netdev; + adapter->mii.mdio_read = atl1c_mdio_read; + adapter->mii.mdio_write = atl1c_mdio_write; + adapter->mii.phy_id_mask = 0x1f; + adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK; + netif_napi_add(netdev, &adapter->napi, atl1c_clean, 64); + setup_timer(&adapter->phy_config_timer, atl1c_phy_config, + (unsigned long)adapter); + /* setup the private structure */ + err = atl1c_sw_init(adapter); + if (err) { + dev_err(&pdev->dev, "net device private data init failed\n"); + goto err_sw_init; + } + atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE | + ATL1C_PCIE_PHY_RESET); + + /* Init GPHY as early as possible due to power saving issue */ + atl1c_phy_reset(&adapter->hw); + + err = atl1c_reset_mac(&adapter->hw); + if (err) { + err = -EIO; + goto err_reset; + } + + device_init_wakeup(&pdev->dev, 1); + /* reset the controller to + * put the device in a known good starting state */ + err = atl1c_phy_init(&adapter->hw); + if (err) { + err = -EIO; + goto err_reset; + } + if (atl1c_read_mac_addr(&adapter->hw) != 0) { + err = -EIO; + dev_err(&pdev->dev, "get mac address failed\n"); + goto err_eeprom; + } + memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); + memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); + if (netif_msg_probe(adapter)) + dev_dbg(&pdev->dev, + "mac address : %02x-%02x-%02x-%02x-%02x-%02x\n", + adapter->hw.mac_addr[0], adapter->hw.mac_addr[1], + adapter->hw.mac_addr[2], adapter->hw.mac_addr[3], + adapter->hw.mac_addr[4], adapter->hw.mac_addr[5]); + + atl1c_hw_set_mac_addr(&adapter->hw); + INIT_WORK(&adapter->reset_task, atl1c_reset_task); + INIT_WORK(&adapter->link_chg_task, atl1c_link_chg_task); + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "register netdevice failed\n"); + goto err_register; + } + + if (netif_msg_probe(adapter)) + dev_info(&pdev->dev, "version %s\n", ATL1C_DRV_VERSION); + cards_found++; + return 0; + +err_reset: +err_register: +err_sw_init: +err_eeprom: + iounmap(adapter->hw.hw_addr); +err_init_netdev: +err_ioremap: + free_netdev(netdev); +err_alloc_etherdev: + pci_release_regions(pdev); +err_pci_reg: +err_dma: + pci_disable_device(pdev); + return err; +} + +/* + * atl1c_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * atl1c_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + */ +static void __devexit atl1c_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + unregister_netdev(netdev); + atl1c_phy_disable(&adapter->hw); + + iounmap(adapter->hw.hw_addr); + + pci_release_regions(pdev); + pci_disable_device(pdev); + free_netdev(netdev); +} + +/* + * atl1c_io_error_detected - called when PCI error is detected + * @pdev: Pointer to PCI device + * @state: The current pci connection state + * + * This function is called after a PCI bus error affecting + * this device has been detected. + */ +static pci_ers_result_t atl1c_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + netif_device_detach(netdev); + + if (netif_running(netdev)) + atl1c_down(adapter); + + pci_disable_device(pdev); + + /* Request a slot slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +/* + * atl1c_io_slot_reset - called after the pci bus has been reset. + * @pdev: Pointer to PCI device + * + * Restart the card from scratch, as if from a cold-boot. Implementation + * resembles the first-half of the e1000_resume routine. + */ +static pci_ers_result_t atl1c_io_slot_reset(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (pci_enable_device(pdev)) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "Cannot re-enable PCI device after reset\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + pci_set_master(pdev); + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + atl1c_reset_mac(&adapter->hw); + + return PCI_ERS_RESULT_RECOVERED; +} + +/* + * atl1c_io_resume - called when traffic can start flowing again. + * @pdev: Pointer to PCI device + * + * This callback is called when the error recovery driver tells us that + * its OK to resume normal operation. Implementation resembles the + * second-half of the atl1c_resume routine. + */ +static void atl1c_io_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (netif_running(netdev)) { + if (atl1c_up(adapter)) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "Cannot bring device back up after reset\n"); + return; + } + } + + netif_device_attach(netdev); +} + +static struct pci_error_handlers atl1c_err_handler = { + .error_detected = atl1c_io_error_detected, + .slot_reset = atl1c_io_slot_reset, + .resume = atl1c_io_resume, +}; + +static struct pci_driver atl1c_driver = { + .name = atl1c_driver_name, + .id_table = atl1c_pci_tbl, + .probe = atl1c_probe, + .remove = __devexit_p(atl1c_remove), + /* Power Managment Hooks */ + .suspend = atl1c_suspend, + .resume = atl1c_resume, + .shutdown = atl1c_shutdown, + .err_handler = &atl1c_err_handler +}; + +/* + * atl1c_init_module - Driver Registration Routine + * + * atl1c_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + */ +static int __init atl1c_init_module(void) +{ + return pci_register_driver(&atl1c_driver); +} + +/* + * atl1c_exit_module - Driver Exit Cleanup Routine + * + * atl1c_exit_module is called just before the driver is removed + * from memory. + */ +static void __exit atl1c_exit_module(void) +{ + pci_unregister_driver(&atl1c_driver); +} + +module_init(atl1c_init_module); +module_exit(atl1c_exit_module); -- cgit v1.2.3 From 82a5bd6a7b1b60b5d357e2e4b93b914f57314016 Mon Sep 17 00:00:00 2001 From: Sebastian Siewior <sebastian@breakpoint.cc> Date: Sat, 14 Feb 2009 23:26:18 +0000 Subject: net/mv643xx: use GFP_ATOMIC while atomic dev_set_rx_mode() grabs netif_addr_lock_bh(): |BUG: sleeping function called from invalid context at /home/bigeasy/git/cryptodev-2.6/mm/slub.c:1599 |in_atomic(): 1, irqs_disabled(): 0, pid: 859, name: ifconfig |2 locks held by ifconfig/859: | #0: (rtnl_mutex){--..}, at: [<c0239ccc>] rtnl_lock+0x18/0x20 | #1: (_xmit_ETHER){-...}, at: [<c022d094>] dev_set_rx_mode+0x1c/0x30 |[<c029f118>] (dump_stack+0x0/0x14) from [<c003df28>] (__might_sleep+0x11c/0x13c) |[<c003de0c>] (__might_sleep+0x0/0x13c) from [<c00a8854>] (kmem_cache_alloc+0x30/0xd4) | r5:c78093a0 r4:c034a47c |[<c00a8824>] (kmem_cache_alloc+0x0/0xd4) from [<c01a5fd0>] (mv643xx_eth_set_rx_mode+0x70/0x188) |[<c01a5f60>] (mv643xx_eth_set_rx_mode+0x0/0x188) from [<c022ced0>] (__dev_set_rx_mode+0x40/0xac) |[<c022ce90>] (__dev_set_rx_mode+0x0/0xac) from [<c022d09c>] (dev_set_rx_mode+0x24/0x30) | r6:00001043 r5:c78090f8 r4:c7809000 |[<c022d078>] (dev_set_rx_mode+0x0/0x30) from [<c02304c4>] (dev_open+0xe4/0x114) | r5:c7809350 r4:c7809000 |[<c02303e0>] (dev_open+0x0/0x114) from [<c022fd18>] (dev_change_flags+0xb0/0x190) | r5:00000041 r4:c7809000 |[<c022fc68>] (dev_change_flags+0x0/0x190) from [<c0270250>] (devinet_ioctl+0x2f0/0x710) | r7:c7221e70 r6:c7aadb00 r5:00000000 r4:00000001 |[<c026ff60>] (devinet_ioctl+0x0/0x710) from [<c02717c8>] (inet_ioctl+0xd4/0x110) |[<c02716f4>] (inet_ioctl+0x0/0x110) from [<c021fb74>] (sock_ioctl+0x1f4/0x254) | r4:c7242b40 |[<c021f980>] (sock_ioctl+0x0/0x254) from [<c00b8160>] (vfs_ioctl+0x38/0x98) | r6:beec9bb8 r5:00008914 r4:c7242b40 |[<c00b8128>] (vfs_ioctl+0x0/0x98) from [<c00b873c>] (do_vfs_ioctl+0x484/0x4d4) | r6:00008914 r5:c7242b40 r4:c74db1c0 |[<c00b82b8>] (do_vfs_ioctl+0x0/0x4d4) from [<c00b87cc>] (sys_ioctl+0x40/0x64) |[<c00b878c>] (sys_ioctl+0x0/0x64) from [<c00269a0>] (ret_fast_syscall+0x0/0x2c) |[42949399.520000] r7:00000036 r6:beec9c80 r5:00000041 r4:beec9bb8 Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> Acked-by: Lennert Buytenhek <buytenh@marvell.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/mv643xx_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 5f31bbb614af..b6ab46942b98 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1575,7 +1575,7 @@ oom: return; } - mc_spec = kmalloc(0x200, GFP_KERNEL); + mc_spec = kmalloc(0x200, GFP_ATOMIC); if (mc_spec == NULL) goto oom; mc_other = mc_spec + (0x100 >> 2); -- cgit v1.2.3 From 57e8f26a10ac4af488292199bb0435555f6723f3 Mon Sep 17 00:00:00 2001 From: Sebastian Siewior <sebastian@breakpoint.cc> Date: Mon, 16 Feb 2009 11:28:15 +0000 Subject: net/mv643xx: don't disable the mib timer too early and lock properly mib_counters_update() also restarts the timer. So the timer is dequeued, the stats are read and then the timer is enqueued again. This is "okay" unless someone unloads the module. The locking here is also broken: mib_counters_update() grabs just a simple spinlock. The only thing the lock is good for is to protect the timer func against other callers namely mv643xx_eth_stop() && mv643xx_eth_get_ethtool_stats(). That means if the spinlock is taken via the ethtool path and than the timer kicks in then the box will lock up. Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> Acked-by: Lennert Buytenhek <buytenh@marvell.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/mv643xx_eth.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index b6ab46942b98..13f11f402a99 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1175,7 +1175,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) { struct mib_counters *p = &mp->mib_counters; - spin_lock(&mp->mib_counters_lock); + spin_lock_bh(&mp->mib_counters_lock); p->good_octets_received += mib_read(mp, 0x00); p->good_octets_received += (u64)mib_read(mp, 0x04) << 32; p->bad_octets_received += mib_read(mp, 0x08); @@ -1208,7 +1208,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) p->bad_crc_event += mib_read(mp, 0x74); p->collision += mib_read(mp, 0x78); p->late_collision += mib_read(mp, 0x7c); - spin_unlock(&mp->mib_counters_lock); + spin_unlock_bh(&mp->mib_counters_lock); mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ); } @@ -2216,8 +2216,6 @@ static int mv643xx_eth_stop(struct net_device *dev) wrlp(mp, INT_MASK, 0x00000000); rdlp(mp, INT_MASK); - del_timer_sync(&mp->mib_counters_timer); - napi_disable(&mp->napi); del_timer_sync(&mp->rx_oom); @@ -2229,6 +2227,7 @@ static int mv643xx_eth_stop(struct net_device *dev) port_reset(mp); mv643xx_eth_get_stats(dev); mib_counters_update(mp); + del_timer_sync(&mp->mib_counters_timer); skb_queue_purge(&mp->rx_recycle); -- cgit v1.2.3 From 858671f80ae5db68d6bcd2c6d3a13e366040ba9b Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Wed, 18 Feb 2009 17:41:38 -0800 Subject: ATM: misplaced parentheses? Add missing parentheses Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/atm/lanai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index 144a49f15220..8733a2ea04c2 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -901,7 +901,7 @@ static int __devinit eeprom_read(struct lanai_dev *lanai) clock_l(); udelay(5); for (i = 128; i != 0; i >>= 1) { /* write command out */ tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) | - (data & i) ? CONFIG1_PROMDATA : 0; + ((data & i) ? CONFIG1_PROMDATA : 0); if (lanai->conf1 != tmp) { set_config1(tmp); udelay(5); /* Let new data settle */ -- cgit v1.2.3 From f72b534961ac38dde17824d7693292eeaadf10e8 Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Wed, 18 Feb 2009 17:42:42 -0800 Subject: TG3: &&/|| confusion phyid Can't be both TG3_PHY_OUI_1 and TG3_PHY_OUI_2 and TG3_PHY_OUI_3. Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Acked-by: Matt Carlson <mcarlson@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/tg3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 4595962fb8e1..b080f9493d83 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -2237,8 +2237,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) phyid = phydev->drv->phy_id & phydev->drv->phy_id_mask; if (phyid != TG3_PHY_ID_BCMAC131) { phyid &= TG3_PHY_OUI_MASK; - if (phyid == TG3_PHY_OUI_1 && - phyid == TG3_PHY_OUI_2 && + if (phyid == TG3_PHY_OUI_1 || + phyid == TG3_PHY_OUI_2 || phyid == TG3_PHY_OUI_3) do_low_power = true; } -- cgit v1.2.3 From ce03aaddd4d67371494b36c8e8a57bc789e934d6 Mon Sep 17 00:00:00 2001 From: Divy Le Ray <divy@chelsio.com> Date: Wed, 18 Feb 2009 17:47:57 -0800 Subject: cxgb3: Add support for PCI ID 0x35. Add support for adapters with a PCI id equal to 0x35. Signed-off-by: Divy Le Ray <divy@chelsio.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/cxgb3/cxgb3_main.c | 1 + drivers/net/cxgb3/t3_hw.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 0089746b8d02..bab8a934c33d 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -90,6 +90,7 @@ static const struct pci_device_id cxgb3_pci_tbl[] = { CH_DEVICE(0x30, 2), /* T3B10 */ CH_DEVICE(0x31, 3), /* T3B20 */ CH_DEVICE(0x32, 1), /* T3B02 */ + CH_DEVICE(0x35, 6), /* T3C20-derived T3C10 */ {0,} }; diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 2d1433077a8e..ac2a974dfe37 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -512,6 +512,13 @@ static const struct adapter_info t3_adap_info[] = { F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, { S_GPIO9, S_GPIO3 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI, &mi1_mdio_ext_ops, "Chelsio T320"}, + {}, + {}, + {1, 0, + F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO6_OEN | F_GPIO7_OEN | + F_GPIO10_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, + { S_GPIO9 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI, + &mi1_mdio_ext_ops, "Chelsio T310" }, }; /* -- cgit v1.2.3 From 22eb36f49e24e922ca6594a99157a3fcb92d3824 Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Thu, 19 Feb 2009 11:57:46 +0100 Subject: [ARM] 5403/1: pxa25x_ep_fifo_flush() *ep->reg_udccs always set to 0 *ep->reg_udccs is always set to 0. Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Acked-by: Eric Miao <eric.miao@marvell.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> --- drivers/usb/gadget/pxa25x_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 9b36205c5759..0ce4e2819847 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -904,8 +904,8 @@ static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) /* most IN status is the same, but ISO can't stall */ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR - | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) - ? 0 : UDCCS_BI_SST; + | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + ? 0 : UDCCS_BI_SST); } -- cgit v1.2.3 From e2e5a0f2b100a5204d27def8bbf73333d1710be2 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Date: Thu, 19 Feb 2009 15:18:59 +0100 Subject: [S390] sclp: handle empty event buffers Handle a malformed hardware response which some versions of the Support Element (SE) may present during SE restart and which otherwise would result in an endless loop in function sclp_dispatch_evbufs. Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> --- drivers/s390/char/sclp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 1fd8f2193ed8..4377e93a43d7 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -280,8 +280,11 @@ sclp_dispatch_evbufs(struct sccb_header *sccb) rc = 0; for (offset = sizeof(struct sccb_header); offset < sccb->length; offset += evbuf->length) { - /* Search for event handler */ evbuf = (struct evbuf_header *) ((addr_t) sccb + offset); + /* Check for malformed hardware response */ + if (evbuf->length == 0) + break; + /* Search for event handler */ reg = NULL; list_for_each(l, &sclp_reg_list) { reg = list_entry(l, struct sclp_register, list); -- cgit v1.2.3 From 23d75d9cadd79bc9fd6553857d57c679cf18d4cb Mon Sep 17 00:00:00 2001 From: Heiko Carstens <heiko.carstens@de.ibm.com> Date: Thu, 19 Feb 2009 15:19:01 +0100 Subject: [S390] fix "mem=" handling in case of standby memory Standby memory detected with the sclp interface gets always registered with add_memory calls without considering the limitationt that the "mem=" kernel paramater implies. So fix this and only register standby memory that is below the specified limit. This fixes zfcpdump since it uses "mem=32M". In case there is appr. 2GB standby memory present all of usable memory would be used for the struct pages needed for standby memory. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> --- arch/s390/include/asm/setup.h | 2 ++ arch/s390/kernel/setup.c | 9 +++++++-- drivers/s390/char/sclp_cmd.c | 5 +++++ 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 2bd9faeb3919..e8bd6ac22c99 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -43,6 +43,8 @@ struct mem_chunk { extern struct mem_chunk memory_chunk[]; extern unsigned long real_memory_size; +extern int memory_end_set; +extern unsigned long memory_end; void detect_memory_layout(struct mem_chunk chunk[]); diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index d825f4950e4e..c5cfb6185eac 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -82,7 +82,9 @@ char elf_platform[ELF_PLATFORM_SIZE]; struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS]; volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ -static unsigned long __initdata memory_end; + +int __initdata memory_end_set; +unsigned long __initdata memory_end; /* * This is set up by the setup-routine at boot-time @@ -281,6 +283,7 @@ void (*pm_power_off)(void) = machine_power_off; static int __init early_parse_mem(char *p) { memory_end = memparse(p, &p); + memory_end_set = 1; return 0; } early_param("mem", early_parse_mem); @@ -508,8 +511,10 @@ static void __init setup_memory_end(void) int i; #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) - if (ipl_info.type == IPL_TYPE_FCP_DUMP) + if (ipl_info.type == IPL_TYPE_FCP_DUMP) { memory_end = ZFCPDUMP_HSA_SIZE; + memory_end_set = 1; + } #endif memory_size = 0; memory_end &= PAGE_MASK; diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 506390496416..77ab6e34a100 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -19,6 +19,7 @@ #include <linux/memory.h> #include <asm/chpid.h> #include <asm/sclp.h> +#include <asm/setup.h> #include "sclp.h" @@ -474,6 +475,10 @@ static void __init add_memory_merged(u16 rn) goto skip_add; if (start + size > VMEM_MAX_PHYS) size = VMEM_MAX_PHYS - start; + if (memory_end_set && (start >= memory_end)) + goto skip_add; + if (memory_end_set && (start + size > memory_end)) + size = memory_end - start; add_memory(0, start, size); skip_add: first_rn = rn; -- cgit v1.2.3 From 005568be363b90c9333c3bcbc1e7a53922816322 Mon Sep 17 00:00:00 2001 From: Tobias Klauser <tklauser@distanz.ch> Date: Mon, 9 Feb 2009 22:02:42 +0100 Subject: drm/i915: Storage class should be before const qualifier The C99 specification states in section 6.11.5: The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature. Signed-off-by: Tobias Klauser <tklauser@distanz.ch> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/intel_sdvo.c | 2 +- drivers/gpu/drm/i915/intel_tv.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index a30508b639ba..fbe6f3931b1b 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -193,7 +193,7 @@ static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr, #define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} /** Mapping of command numbers to names, for debug output */ -const static struct _sdvo_cmd_name { +static const struct _sdvo_cmd_name { u8 cmd; char *name; } sdvo_cmd_names[] = { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index fbb35dc56f5c..56485d67369b 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -411,7 +411,7 @@ struct tv_mode { * These values account for -1s required. */ -const static struct tv_mode tv_modes[] = { +static const struct tv_mode tv_modes[] = { { .name = "NTSC-M", .clock = 107520, -- cgit v1.2.3 From ad45aa9e6e010283bbd8cf0c6309866233e113f2 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Mon, 9 Feb 2009 11:31:41 +0000 Subject: drm: Potential use-after-free on error path. Remove the member from the hash table before we free the structure! Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/drm_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 6915fb82d0b0..308fe1e207f5 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -104,8 +104,8 @@ drm_gem_init(struct drm_device *dev) if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START, DRM_FILE_PAGE_OFFSET_SIZE)) { - drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM); drm_ht_remove(&mm->offset_hash); + drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM); return -ENOMEM; } -- cgit v1.2.3 From 3e49c4f4cf786b70bbc369b99e590de4bebac1b3 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Mon, 9 Feb 2009 11:31:41 +0000 Subject: drm: Free the object ref on error. Ensure that the object is unreferenced if we fail to allocate during drm_gem_flink_ioctl(). Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/drm_gem.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 308fe1e207f5..e5a8ebf9a662 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -295,8 +295,10 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, return -EBADF; again: - if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) - return -ENOMEM; + if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) { + ret = -ENOMEM; + goto err; + } spin_lock(&dev->object_name_lock); if (obj->name) { @@ -310,12 +312,8 @@ again: if (ret == -EAGAIN) goto again; - if (ret != 0) { - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); - return ret; - } + if (ret != 0) + goto err; /* * Leave the reference from the lookup around as the @@ -324,6 +322,12 @@ again: args->name = (uint64_t) obj->name; return 0; + +err: + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; } /** -- cgit v1.2.3 From a198bc80ae59cf7c6da93bc8bd017b2198148ed7 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Fri, 6 Feb 2009 16:55:20 +0000 Subject: drm/i915: Cleanup trivial leak on execbuffer error path. Also spotted by Owain Ainsworth. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 818576654092..b79ced8f3c61 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2480,13 +2480,15 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, if (dev_priv->mm.wedged) { DRM_ERROR("Execbuf while wedged\n"); mutex_unlock(&dev->struct_mutex); - return -EIO; + ret = -EIO; + goto pre_mutex_err; } if (dev_priv->mm.suspended) { DRM_ERROR("Execbuf while VT-switched.\n"); mutex_unlock(&dev->struct_mutex); - return -EBUSY; + ret = -EBUSY; + goto pre_mutex_err; } /* Look up object handles */ -- cgit v1.2.3 From d6873102fd36c577f88174d8bd50f1d51645fc51 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Sun, 8 Feb 2009 19:07:51 +0000 Subject: drm/i915: hold mutex for unreference() in i915_gem_tiling.c Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem_tiling.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index fa1685cba840..7fb4191ef934 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -299,9 +299,8 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } obj_priv->stride = args->stride; - mutex_unlock(&dev->struct_mutex); - drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return 0; } @@ -340,9 +339,8 @@ i915_gem_get_tiling(struct drm_device *dev, void *data, DRM_ERROR("unknown tiling mode\n"); } - mutex_unlock(&dev->struct_mutex); - drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return 0; } -- cgit v1.2.3 From 96dec61d563fb8dff2c8427fdf85327a95b65c74 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Sun, 8 Feb 2009 19:08:04 +0000 Subject: drm/i915: refleak along pin() error path. A missing unreference if the user calls pin() a second time on a pinned buffer. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b79ced8f3c61..55f4c060fa01 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2755,6 +2755,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) { DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n", args->handle); + drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return -EINVAL; } -- cgit v1.2.3 From a35f2e2b83a789e189a501ebd722bc9a1310eb05 Mon Sep 17 00:00:00 2001 From: Roland Dreier <rdreier@cisco.com> Date: Fri, 6 Feb 2009 17:48:09 -0800 Subject: drm/i915: Fix potential AB-BA deadlock in i915_gem_execbuffer() Lockdep warns that i915_gem_execbuffer() can trigger a page fault (which takes mmap_sem) while holding dev->struct_mutex, while drm_vm_open() (which is called with mmap_sem already held) takes dev->struct_mutex. So this is a potential AB-BA deadlock. The way that i915_gem_execbuffer() triggers a page fault is by doing copy_to_user() when returning new buffer offsets back to userspace; however there is no reason to hold the struct_mutex when doing this copy, since what is being copied is the contents of an array private to i915_gem_execbuffer() anyway. So we can fix the potential deadlock (and get rid of the lockdep warning) by simply moving the copy_to_user() outside of where struct_mutex is held. This fixes <http://bugzilla.kernel.org/show_bug.cgi?id=12491>. Reported-by: Jesse Brandeburg <jesse.brandeburg@intel.com> Tested-by: Jesse Brandeburg <jesse.brandeburg@intel.com> Signed-off-by: Roland Dreier <rolandd@cisco.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 55f4c060fa01..ff0d94dd3550 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2634,15 +2634,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, i915_verify_inactive(dev, __FILE__, __LINE__); - /* Copy the new buffer offsets back to the user's exec list. */ - ret = copy_to_user((struct drm_i915_relocation_entry __user *) - (uintptr_t) args->buffers_ptr, - exec_list, - sizeof(*exec_list) * args->buffer_count); - if (ret) - DRM_ERROR("failed to copy %d exec entries " - "back to user (%d)\n", - args->buffer_count, ret); err: for (i = 0; i < pinned; i++) i915_gem_object_unpin(object_list[i]); @@ -2652,6 +2643,18 @@ err: mutex_unlock(&dev->struct_mutex); + if (!ret) { + /* Copy the new buffer offsets back to the user's exec list. */ + ret = copy_to_user((struct drm_i915_relocation_entry __user *) + (uintptr_t) args->buffers_ptr, + exec_list, + sizeof(*exec_list) * args->buffer_count); + if (ret) + DRM_ERROR("failed to copy %d exec entries " + "back to user (%d)\n", + args->buffer_count, ret); + } + pre_mutex_err: drm_free(object_list, sizeof(*object_list) * args->buffer_count, DRM_MEM_DRIVER); -- cgit v1.2.3 From 8d59bae5d9aae10ab230561519bfb97962509bcb Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:26:28 +0000 Subject: drm: Do not leak a new reference for flink() on an existing name The name table should only hold a single reference, so avoid leaking additional references for secondary calls to flink(). Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/drm_gem.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e5a8ebf9a662..5dad6b9d0dec 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -301,27 +301,25 @@ again: } spin_lock(&dev->object_name_lock); - if (obj->name) { - args->name = obj->name; + if (!obj->name) { + ret = idr_get_new_above(&dev->object_name_idr, obj, 1, + &obj->name); + args->name = (uint64_t) obj->name; spin_unlock(&dev->object_name_lock); - return 0; - } - ret = idr_get_new_above(&dev->object_name_idr, obj, 1, - &obj->name); - spin_unlock(&dev->object_name_lock); - if (ret == -EAGAIN) - goto again; - if (ret != 0) - goto err; + if (ret == -EAGAIN) + goto again; - /* - * Leave the reference from the lookup around as the - * name table now holds one - */ - args->name = (uint64_t) obj->name; + if (ret != 0) + goto err; - return 0; + /* Allocate a reference for the name table. */ + drm_gem_object_reference(obj); + } else { + args->name = (uint64_t) obj->name; + spin_unlock(&dev->object_name_lock); + ret = 0; + } err: mutex_lock(&dev->struct_mutex); @@ -452,6 +450,7 @@ drm_gem_object_handle_free(struct kref *kref) spin_lock(&dev->object_name_lock); if (obj->name) { idr_remove(&dev->object_name_idr, obj->name); + obj->name = 0; spin_unlock(&dev->object_name_lock); /* * The object name held a reference to this object, drop -- cgit v1.2.3 From 2ebed176a7ee126448d34fc336afb2ea0238c280 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:26:30 +0000 Subject: drm/i915: Set framebuffer alignment based upon the fence constraints. Set the request alignment to 0, and leave it up to i915_gem_object_pin() to set the appropriate alignment to match the fence covering the object. Eric Anholt mentioned that the pinning code is meant to choose the maximum of the request alignment and that of the fence covering the object... However currently, the pinning code will only apply the fence constraints if the supplied alignment is 0. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/intel_display.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bbdd72909a11..a440d0db5ccc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -377,10 +377,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, alignment = 64 * 1024; break; case I915_TILING_X: - if (IS_I9XX(dev)) - alignment = 1024 * 1024; - else - alignment = 512 * 1024; + /* pin() will align the object as required by fence */ + alignment = 0; break; case I915_TILING_Y: /* FIXME: Is this true? */ -- cgit v1.2.3 From 13af10627676879d1b20ee3cdba9a28f0906dd98 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:26:31 +0000 Subject: drm/i915: Release and unlock on mmap_gtt error path. We failed to unlock the mutex after failing to create the mmap offset. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ff0d94dd3550..f565b6afe414 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -758,8 +758,11 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, if (!obj_priv->mmap_offset) { ret = i915_gem_create_mmap_offset(obj); - if (ret) + if (ret) { + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return ret; + } } args->offset = obj_priv->mmap_offset; -- cgit v1.2.3 From 491152b8778d7d290579c989e8607892accde920 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:26:32 +0000 Subject: drm/i915: unpin for an invalid memory domain. A missing unreference and unpin after rejecting the relocation for an invalid memory domain. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f565b6afe414..0bec4e6d3369 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2254,6 +2254,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, (int) reloc.offset, reloc.read_domains, reloc.write_domain); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); return -EINVAL; } -- cgit v1.2.3 From 47ed185a777632063d2748f59d14ec6fdeb26f67 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:26:33 +0000 Subject: drm/i915: Unpin the ringbuffer if we fail to ioremap it. A missing unpin on the error path. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0bec4e6d3369..e5fc664337f3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3159,6 +3159,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev) if (ring->map.handle == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); + i915_gem_object_unpin(obj); drm_gem_object_unreference(obj); return -EINVAL; } -- cgit v1.2.3 From 3eb2ee77b0b6b7b2c10308d7b46d2a459fb5be10 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:26:34 +0000 Subject: drm/i915: Unpin the hws if we fail to kmap. A missing unpin on the error path. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e5fc664337f3..fd4b4e268a22 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3106,6 +3106,7 @@ i915_gem_init_hws(struct drm_device *dev) if (dev_priv->hw_status_page == NULL) { DRM_ERROR("Failed to map status page.\n"); memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); + i915_gem_object_unpin(obj); drm_gem_object_unreference(obj); return -EINVAL; } -- cgit v1.2.3 From b4476f52e43fadcb9402723a1a55ba1308757525 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:26:36 +0000 Subject: drm/i915: Unpin the fb on error during construction. If we fail whilst constructing the fb, then we need to unpin it as well. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/intel_fb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index afd1217b8a02..b7f0ebe9f810 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -473,7 +473,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, ret = intel_framebuffer_create(dev, &mode_cmd, &fb, fbo); if (ret) { DRM_ERROR("failed to allocate fb.\n"); - goto out_unref; + goto out_unpin; } list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); @@ -484,7 +484,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, info = framebuffer_alloc(sizeof(struct intelfb_par), device); if (!info) { ret = -ENOMEM; - goto out_unref; + goto out_unpin; } par = info->par; @@ -513,7 +513,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, size); if (!info->screen_base) { ret = -ENOSPC; - goto out_unref; + goto out_unpin; } info->screen_size = size; @@ -608,6 +608,8 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, mutex_unlock(&dev->struct_mutex); return 0; +out_unpin: + i915_gem_object_unpin(fbo); out_unref: drm_gem_object_unreference(fbo); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.3 From ea39f835168f60b01e59d0f348da25d297e7cf94 Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg <krh@redhat.com> Date: Thu, 12 Feb 2009 14:37:56 -0500 Subject: drm: Release user fbs in drm_release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids leaking fbs and associated buffers on release. Signed-off-by: Kristian Høgsberg <krh@redhat.com> Tested-by: Tested-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/drm_crtc.c | 3 +-- drivers/gpu/drm/drm_fops.c | 3 +++ include/drm/drm_crtc.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index bfce0992fefb..94a768871734 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1741,9 +1741,8 @@ out: * RETURNS: * Zero on success, errno on failure. */ -void drm_fb_release(struct file *filp) +void drm_fb_release(struct drm_file *priv) { - struct drm_file *priv = filp->private_data; struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index b06a53715853..6c020fe5431c 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -457,6 +457,9 @@ int drm_release(struct inode *inode, struct file *filp) if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, file_priv); + if (dev->driver->driver_features & DRIVER_MODESET) + drm_fb_release(file_priv); + mutex_lock(&dev->ctxlist_mutex); if (!list_empty(&dev->ctxlist)) { struct drm_ctx_list *pos, *n; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d54de24bf371..5ded1acfb543 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -609,7 +609,7 @@ extern char *drm_get_dvi_i_subconnector_name(int val); extern char *drm_get_dvi_i_select_name(int val); extern char *drm_get_tv_subconnector_name(int val); extern char *drm_get_tv_select_name(int val); -extern void drm_fb_release(struct file *filp); +extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); -- cgit v1.2.3 From 67eabc0553a32c491fdb392ff2358a0384562050 Mon Sep 17 00:00:00 2001 From: Steve Aarnio <steve.j.aarnio@linux.intel.com> Date: Thu, 12 Feb 2009 11:34:02 -0800 Subject: drm/i915: Don't add panel_fixed_mode to the probed modes list at LVDS init. In the case where no EDID data is read from the device, adding the panel_fixed_mode pointer to the probed modes list causes data corruption. If the panel_fixed_mode pointer is added to the probed modes list at init time, a copy of the mode is added again at drm_get_modes() request time. Then, the panel_fixed_mode pointer is freed because it is seen as a duplicate mode. Unfortunately, this pointer is still stored and used in mode_fixup(). Because the panel_fixed_mode data is copied and returned at drm_get_modes() time, it is unnecessary to add this information at init time. Signed-off-by: Steve Aarnio <steve.j.aarnio@intel.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/intel_lvds.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 6d4f91265354..0d211af98854 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -481,8 +481,6 @@ void intel_lvds_init(struct drm_device *dev) if (dev_priv->panel_fixed_mode) { dev_priv->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; - drm_mode_probed_add(connector, - dev_priv->panel_fixed_mode); goto out; } } -- cgit v1.2.3 From 85a7bb98582b60b7e9130159d2464eb0bbac13f7 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 14:52:44 +0000 Subject: drm/i915: Cleanup the hws on ringbuffer constrution failure. If we fail to create the ringbuffer, then we need to cleanup the allocated hws. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/i915/i915_gem.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fd4b4e268a22..078858178832 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3119,6 +3119,27 @@ i915_gem_init_hws(struct drm_device *dev) return 0; } +static void +i915_gem_cleanup_hws(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj = dev_priv->hws_obj; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + if (dev_priv->hws_obj == NULL) + return; + + kunmap(obj_priv->page_list[0]); + i915_gem_object_unpin(obj); + drm_gem_object_unreference(obj); + dev_priv->hws_obj = NULL; + memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); + dev_priv->hw_status_page = NULL; + + /* Write high address into HWS_PGA when disabling. */ + I915_WRITE(HWS_PGA, 0x1ffff000); +} + int i915_gem_init_ringbuffer(struct drm_device *dev) { @@ -3136,6 +3157,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev) obj = drm_gem_object_alloc(dev, 128 * 1024); if (obj == NULL) { DRM_ERROR("Failed to allocate ringbuffer\n"); + i915_gem_cleanup_hws(dev); return -ENOMEM; } obj_priv = obj->driver_private; @@ -3143,6 +3165,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev) ret = i915_gem_object_pin(obj, 4096); if (ret != 0) { drm_gem_object_unreference(obj); + i915_gem_cleanup_hws(dev); return ret; } @@ -3162,6 +3185,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev) memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); i915_gem_object_unpin(obj); drm_gem_object_unreference(obj); + i915_gem_cleanup_hws(dev); return -EINVAL; } ring->ring_obj = obj; @@ -3241,20 +3265,7 @@ i915_gem_cleanup_ringbuffer(struct drm_device *dev) dev_priv->ring.ring_obj = NULL; memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); - if (dev_priv->hws_obj != NULL) { - struct drm_gem_object *obj = dev_priv->hws_obj; - struct drm_i915_gem_object *obj_priv = obj->driver_private; - - kunmap(obj_priv->page_list[0]); - i915_gem_object_unpin(obj); - drm_gem_object_unreference(obj); - dev_priv->hws_obj = NULL; - memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); - dev_priv->hw_status_page = NULL; - - /* Write high address into HWS_PGA when disabling. */ - I915_WRITE(HWS_PGA, 0x1ffff000); - } + i915_gem_cleanup_hws(dev); } int -- cgit v1.2.3 From e62fb64e6187ea9d8bcedb17ccaa045ed92d4b55 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 16:39:21 +0000 Subject: drm: Check for a NULL encoder when reverting on error path We need to skip the connectors with a NULL encoder to match the success path and avoid an OOPS. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/drm_crtc_helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 964c5eb1fada..8ba22c039a11 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -775,8 +775,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) fail_set_mode: set->crtc->enabled = save_enabled; count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder) + continue; + connector->encoder->crtc = save_crtcs[count++]; + } fail_no_encoder: kfree(save_crtcs); count = 0; -- cgit v1.2.3 From 5c3b82e2b229e78eb32f4ea12d16f3ebeeab3fc7 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 11 Feb 2009 13:25:09 +0000 Subject: drm: Propagate failure from setting crtc base. Check the error paths within intel_pipe_set_base() to first cleanup and then report back the error. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie> --- drivers/gpu/drm/drm_crtc_helper.c | 15 +++++-- drivers/gpu/drm/i915/intel_display.c | 84 +++++++++++++++++++++++------------- include/drm/drm_crtc_helper.h | 10 ++--- 3 files changed, 69 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 8ba22c039a11..733028b4d45e 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -512,8 +512,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (drm_mode_equal(&saved_mode, &crtc->mode)) { if (saved_x != crtc->x || saved_y != crtc->y || depth_changed || bpp_changed) { - crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, - old_fb); + ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, + old_fb); goto done; } } @@ -552,7 +552,9 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, /* Set up the DPLL and any encoders state that needs to adjust or depend * on the DPLL. */ - crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + if (!ret) + goto done; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { @@ -752,6 +754,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) if (!drm_crtc_helper_set_mode(set->crtc, set->mode, set->x, set->y, old_fb)) { + DRM_ERROR("failed to set mode on crtc %p\n", + set->crtc); ret = -EINVAL; goto fail_set_mode; } @@ -765,7 +769,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) old_fb = set->crtc->fb; if (set->crtc->fb != set->fb) set->crtc->fb = set->fb; - crtc_funcs->mode_set_base(set->crtc, set->x, set->y, old_fb); + ret = crtc_funcs->mode_set_base(set->crtc, + set->x, set->y, old_fb); + if (ret != 0) + goto fail_set_mode; } kfree(save_encoders); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a440d0db5ccc..ac92799fa8a1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -343,7 +343,7 @@ intel_wait_for_vblank(struct drm_device *dev) udelay(20000); } -static void +static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { @@ -361,11 +361,21 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr, alignment; + int ret; /* no fb bound */ if (!crtc->fb) { DRM_DEBUG("No FB bound\n"); - return; + return 0; + } + + switch (pipe) { + case 0: + case 1: + break; + default: + DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); + return -EINVAL; } intel_fb = to_intel_framebuffer(crtc->fb); @@ -383,20 +393,24 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, case I915_TILING_Y: /* FIXME: Is this true? */ DRM_ERROR("Y tiled not allowed for scan out buffers\n"); - return; + return -EINVAL; default: BUG(); } - if (i915_gem_object_pin(intel_fb->obj, alignment)) - return; - - i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1); - - Start = obj_priv->gtt_offset; - Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); + mutex_lock(&dev->struct_mutex); + ret = i915_gem_object_pin(intel_fb->obj, alignment); + if (ret != 0) { + mutex_unlock(&dev->struct_mutex); + return ret; + } - I915_WRITE(dspstride, crtc->fb->pitch); + ret = i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1); + if (ret != 0) { + i915_gem_object_unpin(intel_fb->obj); + mutex_unlock(&dev->struct_mutex); + return ret; + } dspcntr = I915_READ(dspcntr_reg); /* Mask out pixel format bits in case we change it */ @@ -417,11 +431,17 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, break; default: DRM_ERROR("Unknown color depth\n"); - return; + i915_gem_object_unpin(intel_fb->obj); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; } I915_WRITE(dspcntr_reg, dspcntr); + Start = obj_priv->gtt_offset; + Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); + DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y); + I915_WRITE(dspstride, crtc->fb->pitch); if (IS_I965G(dev)) { I915_WRITE(dspbase, Offset); I915_READ(dspbase); @@ -438,27 +458,24 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, intel_fb = to_intel_framebuffer(old_fb); i915_gem_object_unpin(intel_fb->obj); } + mutex_unlock(&dev->struct_mutex); if (!dev->primary->master) - return; + return 0; master_priv = dev->primary->master->driver_priv; if (!master_priv->sarea_priv) - return; + return 0; - switch (pipe) { - case 0: - master_priv->sarea_priv->pipeA_x = x; - master_priv->sarea_priv->pipeA_y = y; - break; - case 1: + if (pipe) { master_priv->sarea_priv->pipeB_x = x; master_priv->sarea_priv->pipeB_y = y; - break; - default: - DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); - break; + } else { + master_priv->sarea_priv->pipeA_x = x; + master_priv->sarea_priv->pipeA_y = y; } + + return 0; } @@ -706,11 +723,11 @@ static int intel_panel_fitter_pipe (struct drm_device *dev) return 1; } -static void intel_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) +static int intel_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -737,6 +754,7 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, bool is_crt = false, is_lvds = false, is_tv = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + int ret; drm_vblank_pre_modeset(dev, pipe); @@ -777,7 +795,7 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); if (!ok) { DRM_ERROR("Couldn't find PLL settings for mode!\n"); - return; + return -EINVAL; } fp = clock.n << 16 | clock.m1 << 8 | clock.m2; @@ -948,9 +966,13 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(dspcntr_reg, dspcntr); /* Flush the plane changes */ - intel_pipe_set_base(crtc, x, y, old_fb); + ret = intel_pipe_set_base(crtc, x, y, old_fb); + if (ret != 0) + return ret; drm_vblank_post_modeset(dev, pipe); + + return 0; } /** Loads the palette/gamma unit for the CRTC with the prepared values */ diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 0c6f0e11b41b..0b0d236c2154 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -54,13 +54,13 @@ struct drm_crtc_helper_funcs { struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); /* Actually set the mode */ - void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb); + int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y, + struct drm_framebuffer *old_fb); /* Move the crtc on the current fb to the given position *optional* */ - void (*mode_set_base)(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb); + int (*mode_set_base)(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); }; struct drm_encoder_helper_funcs { -- cgit v1.2.3 From 7f9872e06d749afdc2029aa6b7ffe88cb3b8c5c2 Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg <krh@bitplanet.net> Date: Fri, 13 Feb 2009 20:56:49 -0500 Subject: drm: Add locking around cursor gem operations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to hold the struct_mutex around pinning and the phys object operations. Signed-off-by: Kristian Høgsberg <krh@redhat.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/intel_display.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ac92799fa8a1..94c7c098c4ff 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1043,18 +1043,19 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, } /* we only need to pin inside GTT if cursor is non-phy */ + mutex_lock(&dev->struct_mutex); if (!dev_priv->cursor_needs_physical) { ret = i915_gem_object_pin(bo, PAGE_SIZE); if (ret) { DRM_ERROR("failed to pin cursor bo\n"); - goto fail; + goto fail_locked; } addr = obj_priv->gtt_offset; } else { ret = i915_gem_attach_phys_object(dev, bo, (pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1); if (ret) { DRM_ERROR("failed to attach phys object\n"); - goto fail; + goto fail_locked; } addr = obj_priv->phys_obj->handle->busaddr; } @@ -1074,10 +1075,9 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo); } else i915_gem_object_unpin(intel_crtc->cursor_bo); - mutex_lock(&dev->struct_mutex); drm_gem_object_unreference(intel_crtc->cursor_bo); - mutex_unlock(&dev->struct_mutex); } + mutex_unlock(&dev->struct_mutex); intel_crtc->cursor_addr = addr; intel_crtc->cursor_bo = bo; @@ -1085,6 +1085,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, return 0; fail: mutex_lock(&dev->struct_mutex); +fail_locked: drm_gem_object_unreference(bo); mutex_unlock(&dev->struct_mutex); return ret; -- cgit v1.2.3 From f3cade5c037054ce5f57651fe0b64eaa9781c753 Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg <krh@bitplanet.net> Date: Fri, 13 Feb 2009 20:56:50 -0500 Subject: drm: Bring PLL limits in sync with DDX values. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg <krh@redhat.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/intel_display.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 94c7c098c4ff..4f54ac55f326 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -90,12 +90,12 @@ typedef struct { #define I9XX_DOT_MAX 400000 #define I9XX_VCO_MIN 1400000 #define I9XX_VCO_MAX 2800000 -#define I9XX_N_MIN 3 -#define I9XX_N_MAX 8 +#define I9XX_N_MIN 1 +#define I9XX_N_MAX 6 #define I9XX_M_MIN 70 #define I9XX_M_MAX 120 #define I9XX_M1_MIN 10 -#define I9XX_M1_MAX 20 +#define I9XX_M1_MAX 22 #define I9XX_M2_MIN 5 #define I9XX_M2_MAX 9 #define I9XX_P_SDVO_DAC_MIN 5 -- cgit v1.2.3 From a29f5ca3d691995266a4b1df313e32ff0509a03c Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg <krh@bitplanet.net> Date: Fri, 13 Feb 2009 20:56:51 -0500 Subject: drm: Collapse identical i8xx_clock() and i9xx_clock(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They used to be different. Now they're identical. Signed-off-by: Kristian Høgsberg <krh@redhat.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/intel_display.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4f54ac55f326..8b2038706268 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -189,19 +189,7 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc) return limit; } -/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ - -static void i8xx_clock(int refclk, intel_clock_t *clock) -{ - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); - clock->p = clock->p1 * clock->p2; - clock->vco = refclk * clock->m / (clock->n + 2); - clock->dot = clock->vco / clock->p; -} - -/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ - -static void i9xx_clock(int refclk, intel_clock_t *clock) +static void intel_clock(int refclk, intel_clock_t *clock) { clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); clock->p = clock->p1 * clock->p2; @@ -209,15 +197,6 @@ static void i9xx_clock(int refclk, intel_clock_t *clock) clock->dot = clock->vco / clock->p; } -static void intel_clock(struct drm_device *dev, int refclk, - intel_clock_t *clock) -{ - if (IS_I9XX(dev)) - i9xx_clock (refclk, clock); - else - i8xx_clock (refclk, clock); -} - /** * Returns whether any output on the specified pipe is of the specified type */ @@ -318,7 +297,7 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, clock.p1 <= limit->p1.max; clock.p1++) { int this_err; - intel_clock(dev, refclk, &clock); + intel_clock(refclk, &clock); if (!intel_PLL_is_valid(crtc, &clock)) continue; @@ -1313,7 +1292,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) } /* XXX: Handle the 100Mhz refclk */ - i9xx_clock(96000, &clock); + intel_clock(96000, &clock); } else { bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN); @@ -1325,9 +1304,9 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) { /* XXX: might not be 66MHz */ - i8xx_clock(66000, &clock); + intel_clock(66000, &clock); } else - i8xx_clock(48000, &clock); + intel_clock(48000, &clock); } else { if (dpll & PLL_P1_DIVIDE_BY_TWO) clock.p1 = 2; @@ -1340,7 +1319,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) else clock.p2 = 2; - i8xx_clock(48000, &clock); + intel_clock(48000, &clock); } } -- cgit v1.2.3 From 43565a0648e664744ac9201c199681451355edcc Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg <krh@bitplanet.net> Date: Fri, 13 Feb 2009 20:56:52 -0500 Subject: drm: Use spread spectrum when the bios tells us it's ok. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lifted from the DDX modesetting. Signed-off-by: Kristian Høgsberg <krh@redhat.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_bios.c | 8 ++++++++ drivers/gpu/drm/i915/intel_display.c | 20 ++++++++++++++------ 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7325363164f8..135a08f615cd 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -184,6 +184,8 @@ typedef struct drm_i915_private { unsigned int lvds_dither:1; unsigned int lvds_vbt:1; unsigned int int_crt_support:1; + unsigned int lvds_use_ssc:1; + int lvds_ssc_freq; struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 4ca82a025525..65be30dccc77 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -135,6 +135,14 @@ parse_general_features(struct drm_i915_private *dev_priv, if (general) { dev_priv->int_tv_support = general->int_tv_support; dev_priv->int_crt_support = general->int_crt_support; + dev_priv->lvds_use_ssc = general->enable_ssc; + + if (dev_priv->lvds_use_ssc) { + if (IS_I855(dev_priv->dev)) + dev_priv->lvds_ssc_freq = general->ssc_freq ? 66 : 48; + else + dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 96; + } } } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8b2038706268..13f9b6667c94 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -217,7 +217,7 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type) return false; } -#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } +#define INTELPllInvalid(s) do { DRM_DEBUG(s); return false; } while (0) /** * Returns whether the given set of divisors are valid for a given refclk with * the given connectors. @@ -726,7 +726,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - int refclk; + int refclk, num_outputs = 0; intel_clock_t clock; u32 dpll = 0, fp = 0, dspcntr, pipeconf; bool ok, is_sdvo = false, is_dvo = false; @@ -763,9 +763,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, is_crt = true; break; } + + num_outputs++; } - if (IS_I9XX(dev)) { + if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) { + refclk = dev_priv->lvds_ssc_freq * 1000; + DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000); + } else if (IS_I9XX(dev)) { refclk = 96000; } else { refclk = 48000; @@ -824,11 +829,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } } - if (is_tv) { + if (is_sdvo && is_tv) + dpll |= PLL_REF_INPUT_TVCLKINBC; + else if (is_tv) /* XXX: just matching BIOS for now */ -/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ + /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ dpll |= 3; - } + else if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; else dpll |= PLL_REF_INPUT_DREFCLK; -- cgit v1.2.3 From 496818f08a78476abdb307e241911536221239fc Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Wed, 11 Feb 2009 13:28:14 -0800 Subject: drm/i915: take struct mutex around fb unref Need to do this in case the unref ends up doing a free. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/intel_display.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 13f9b6667c94..4d2baf7b00be 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1606,7 +1606,9 @@ intel_user_framebuffer_create(struct drm_device *dev, ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj); if (ret) { + mutex_lock(&dev->struct_mutex); drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return NULL; } -- cgit v1.2.3 From ab00b3e5210954cbaff9207db874a9f03197e3ba Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Wed, 11 Feb 2009 14:01:46 -0800 Subject: drm/i915: Keep refs on the object over the lifetime of vmas for GTT mmap. This fixes potential fault at fault time if the object was unreferenced while the mapping still existed. Now, while the mmap_offset only lives for the lifetime of the object, the object also stays alive while a vma exists that needs it. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/drm_gem.c | 28 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.c | 2 ++ drivers/gpu/drm/i915/i915_gem.c | 43 ++++++++++++++++++++++++----------------- include/drm/drmP.h | 2 ++ 4 files changed, 57 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 5dad6b9d0dec..88d3368ffddd 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -463,6 +463,26 @@ drm_gem_object_handle_free(struct kref *kref) } EXPORT_SYMBOL(drm_gem_object_handle_free); +void drm_gem_vm_open(struct vm_area_struct *vma) +{ + struct drm_gem_object *obj = vma->vm_private_data; + + drm_gem_object_reference(obj); +} +EXPORT_SYMBOL(drm_gem_vm_open); + +void drm_gem_vm_close(struct vm_area_struct *vma) +{ + struct drm_gem_object *obj = vma->vm_private_data; + struct drm_device *dev = obj->dev; + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); +} +EXPORT_SYMBOL(drm_gem_vm_close); + + /** * drm_gem_mmap - memory map routine for GEM objects * @filp: DRM file pointer @@ -524,6 +544,14 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) #endif vma->vm_page_prot = __pgprot(prot); + /* Take a ref for this mapping of the object, so that the fault + * handler can dereference the mmap offset's pointer to the object. + * This reference is cleaned up by the corresponding vm_close + * (which should happen whether the vma was created by this call, or + * by a vm_open due to mremap or partial unmap or whatever). + */ + drm_gem_object_reference(obj); + vma->vm_file = filp; /* Needed for drm_vm_open() */ drm_vm_open_locked(vma); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index aac12ee31a46..a31cbdbc3c54 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -94,6 +94,8 @@ static int i915_resume(struct drm_device *dev) static struct vm_operations_struct i915_gem_vm_ops = { .fault = i915_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, }; static struct drm_driver driver = { diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 078858178832..ac534c9a2f81 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -607,8 +607,6 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) case -EAGAIN: return VM_FAULT_OOM; case -EFAULT: - case -EBUSY: - DRM_ERROR("can't insert pfn?? fault or busy...\n"); return VM_FAULT_SIGBUS; default: return VM_FAULT_NOPAGE; @@ -684,6 +682,30 @@ out_free_list: return ret; } +static void +i915_gem_free_mmap_offset(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + struct drm_gem_mm *mm = dev->mm_private; + struct drm_map_list *list; + + list = &obj->map_list; + drm_ht_remove_item(&mm->offset_hash, &list->hash); + + if (list->file_offset_node) { + drm_mm_put_block(list->file_offset_node); + list->file_offset_node = NULL; + } + + if (list->map) { + drm_free(list->map, sizeof(struct drm_map), DRM_MEM_DRIVER); + list->map = NULL; + } + + obj_priv->mmap_offset = 0; +} + /** * i915_gem_get_gtt_alignment - return required GTT alignment for an object * @obj: object to check @@ -2896,9 +2918,6 @@ int i915_gem_init_object(struct drm_gem_object *obj) void i915_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_map *map; struct drm_i915_gem_object *obj_priv = obj->driver_private; while (obj_priv->pin_count > 0) @@ -2909,19 +2928,7 @@ void i915_gem_free_object(struct drm_gem_object *obj) i915_gem_object_unbind(obj); - list = &obj->map_list; - drm_ht_remove_item(&mm->offset_hash, &list->hash); - - if (list->file_offset_node) { - drm_mm_put_block(list->file_offset_node); - list->file_offset_node = NULL; - } - - map = list->map; - if (map) { - drm_free(map, sizeof(*map), DRM_MEM_DRIVER); - list->map = NULL; - } + i915_gem_free_mmap_offset(obj); drm_free(obj_priv->page_cpu_valid, 1, DRM_MEM_DRIVER); drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8190b9bcc2d9..e5f4ae989abf 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1321,6 +1321,8 @@ void drm_gem_object_free(struct kref *kref); struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev, size_t size); void drm_gem_object_handle_free(struct kref *kref); +void drm_gem_vm_open(struct vm_area_struct *vma); +void drm_gem_vm_close(struct vm_area_struct *vma); int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); static inline void -- cgit v1.2.3 From 3d16118dc825a654043dfe3e14371fdf2976994d Mon Sep 17 00:00:00 2001 From: etienne <etienne.basset@numericable.fr> Date: Fri, 20 Feb 2009 09:44:45 +1000 Subject: drm/radeon: update sarea copies of last_ variables on resume. This fixes a regression reported in bug #12613. [airlied: not I tweaked the patch slightly and fixed it by etienne did all the hardwork so gets authorship] Signed-off-by: etienne <etienne.basset@numericable.fr> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/radeon/radeon_cp.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index df4cf97e5d97..92965dbb3c14 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -557,8 +557,10 @@ static int radeon_do_engine_reset(struct drm_device * dev) } static void radeon_cp_init_ring_buffer(struct drm_device * dev, - drm_radeon_private_t * dev_priv) + drm_radeon_private_t *dev_priv, + struct drm_file *file_priv) { + struct drm_radeon_master_private *master_priv; u32 ring_start, cur_read_ptr; u32 tmp; @@ -677,6 +679,14 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, dev_priv->scratch[2] = 0; RADEON_WRITE(RADEON_LAST_CLEAR_REG, 0); + /* reset sarea copies of these */ + master_priv = file_priv->master->driver_priv; + if (master_priv->sarea_priv) { + master_priv->sarea_priv->last_frame = 0; + master_priv->sarea_priv->last_dispatch = 0; + master_priv->sarea_priv->last_clear = 0; + } + radeon_do_wait_for_idle(dev_priv); /* Sync everything up */ @@ -1215,7 +1225,7 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, } radeon_cp_load_microcode(dev_priv); - radeon_cp_init_ring_buffer(dev, dev_priv); + radeon_cp_init_ring_buffer(dev, dev_priv, file_priv); dev_priv->last_buf = 0; @@ -1281,7 +1291,7 @@ static int radeon_do_cleanup_cp(struct drm_device * dev) * * Charl P. Botha <http://cpbotha.net> */ -static int radeon_do_resume_cp(struct drm_device * dev) +static int radeon_do_resume_cp(struct drm_device *dev, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; @@ -1304,7 +1314,7 @@ static int radeon_do_resume_cp(struct drm_device * dev) } radeon_cp_load_microcode(dev_priv); - radeon_cp_init_ring_buffer(dev, dev_priv); + radeon_cp_init_ring_buffer(dev, dev_priv, file_priv); radeon_do_engine_reset(dev); radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); @@ -1479,8 +1489,7 @@ int radeon_cp_idle(struct drm_device *dev, void *data, struct drm_file *file_pri */ int radeon_cp_resume(struct drm_device *dev, void *data, struct drm_file *file_priv) { - - return radeon_do_resume_cp(dev); + return radeon_do_resume_cp(dev, file_priv); } int radeon_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv) -- cgit v1.2.3 From 494ef10ebacc23679350a17483879366d8bafebd Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez <inaky@linux.intel.com> Date: Thu, 19 Feb 2009 14:40:29 -0800 Subject: wimax/i2400m: driver loads firmware v1.4 instead of v1.3 This is a one liner change to have the driver use by default the v1.4 of the i2400m firmware instead of v1.3. The v1.4 version of the firmware has been submitted to David Woodhouse for inclusion in the linux-firmware tree and it is already available at http://linuxwimax.org/Download. The reason for this change is that the 1.3 release of the user space software and firmware has a few issues that will make it difficult to use with currently deployed commercial networks such as Xohm and Clearwire. As well, the new 1.4 release of the user space software (which matches the 1.4 firmware) has intermitent issues with the 1.3 firmware. The 1.4 release in http://linuxwimax.org/Download has been widely deployed and tested with the codebase in 2.6.29-rc, the 1.4 firmware and the 1.4 user space components. We understand it is quite late in the rc process for such a change, but would like to ask for the change to be taken into consideration. Alternatively, a user could always force feed a 1.4 firmware into a driver that doesn't have this modification by: $ cd /lib/firmware $ mv i2400m-fw-usb-1.3.sbcf i2400m-fw-usb-1.3.real.sbcf $ ln -sf i2400m-fw-usb-1.4.sbc i2400m-fw-usb-1.3.sbcf Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/wimax/i2400m/i2400m.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 067c871cc226..3b9d27ea2950 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -157,7 +157,7 @@ enum { /* Firmware version we request when pulling the fw image file */ -#define I2400M_FW_VERSION "1.3" +#define I2400M_FW_VERSION "1.4" /** -- cgit v1.2.3 From 9df8f4e3ee760c14211a5f484e9ee4f0bc0c566b Mon Sep 17 00:00:00 2001 From: Steve Glendinning <steve.glendinning@smsc.com> Date: Mon, 16 Feb 2009 07:46:06 +0000 Subject: smsc9420: fix another postfixed timeout Roel Kluin recently fixed several instances where variables reach -1, but 0 is tested afterwards. This patch fixes another, so the timeout will be correctly detected and a warning printed. Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/smsc9420.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index a1e4b3895b33..83938e1953b0 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -341,7 +341,7 @@ static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op) do { msleep(1); e2cmd = smsc9420_reg_read(pd, E2P_CMD); - } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--)); + } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout)); if (!timeout) { smsc_info(HW, "TIMED OUT"); -- cgit v1.2.3 From 62660e28084df3d8067ab855f326d3027808c569 Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Wed, 18 Feb 2009 10:19:50 +0100 Subject: sundance: missing parentheses? Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/sundance.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index feaf0e0577d7..43695b76606f 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -909,7 +909,7 @@ static void check_duplex(struct net_device *dev) printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d " "negotiated capability %4.4x.\n", dev->name, duplex ? "full" : "half", np->phys[0], negotiated); - iowrite16(ioread16(ioaddr + MACCtrl0) | duplex ? 0x20 : 0, ioaddr + MACCtrl0); + iowrite16(ioread16(ioaddr + MACCtrl0) | (duplex ? 0x20 : 0), ioaddr + MACCtrl0); } } -- cgit v1.2.3 From 196b7e1b9cca9e187bb61fa7d60f04f4ab2c0592 Mon Sep 17 00:00:00 2001 From: Steve Glendinning <steve.glendinning@smsc.com> Date: Sun, 15 Feb 2009 22:55:01 +0000 Subject: smsc9420: handle magic field of ethtool_eeprom ethtool.h says the driver should set the magic field in get_eeprom and verify it in set_eeprom. This patch adds this functionality using an arbitary driver-specific magic value constant (0x9420). Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/smsc9420.c | 4 ++++ drivers/net/smsc9420.h | 1 + 2 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index 83938e1953b0..4e15ae068b3f 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -413,6 +413,7 @@ static int smsc9420_ethtool_get_eeprom(struct net_device *dev, } memcpy(data, &eeprom_data[eeprom->offset], len); + eeprom->magic = SMSC9420_EEPROM_MAGIC; eeprom->len = len; return 0; } @@ -423,6 +424,9 @@ static int smsc9420_ethtool_set_eeprom(struct net_device *dev, struct smsc9420_pdata *pd = netdev_priv(dev); int ret; + if (eeprom->magic != SMSC9420_EEPROM_MAGIC) + return -EINVAL; + smsc9420_eeprom_enable_access(pd); smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWEN_); ret = smsc9420_eeprom_write_location(pd, eeprom->offset, *data); diff --git a/drivers/net/smsc9420.h b/drivers/net/smsc9420.h index 69c351f93f86..e441402f77a2 100644 --- a/drivers/net/smsc9420.h +++ b/drivers/net/smsc9420.h @@ -44,6 +44,7 @@ #define LAN_REGISTER_EXTENT (0x400) #define SMSC9420_EEPROM_SIZE ((u32)11) +#define SMSC9420_EEPROM_MAGIC (0x9420) #define PKT_BUF_SZ (VLAN_ETH_FRAME_LEN + NET_IP_ALIGN + 4) -- cgit v1.2.3 From 2cf0dbed27af3f827a96db98c2535002902f6af0 Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Fri, 20 Feb 2009 00:52:19 -0800 Subject: SMSC: timeout reaches -1 With a postfix decrement timeouts will reach -1 rather than 0, so the error path does not appear. Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/smsc911x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 783c1a7b869e..9a78daec2fe9 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -1624,7 +1624,7 @@ static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op) do { msleep(1); e2cmd = smsc911x_reg_read(pdata, E2P_CMD); - } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--)); + } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout)); if (!timeout) { SMSC_TRACE(DRV, "TIMED OUT"); -- cgit v1.2.3 From 0d5048a96fc51d976ac777e3d78762b4dd241693 Mon Sep 17 00:00:00 2001 From: Randy Dunlap <randy.dunlap@oracle.com> Date: Fri, 20 Feb 2009 00:54:44 -0800 Subject: ISDN: fix sc/shmem printk format warning Fix isdn/sc/shmem.c printk format warning: drivers/isdn/sc/shmem.c:57: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/isdn/sc/shmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c index 712220cef139..7f16d75d2d89 100644 --- a/drivers/isdn/sc/shmem.c +++ b/drivers/isdn/sc/shmem.c @@ -54,7 +54,7 @@ void memcpy_toshmem(int card, void *dest, const void *src, size_t n) spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename, ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80); - pr_debug("%s: copying %d bytes from %#lx to %#lx\n", + pr_debug("%s: copying %zu bytes from %#lx to %#lx\n", sc_adapter[card]->devicename, n, (unsigned long) src, sc_adapter[card]->rambase + ((unsigned long) dest %0x4000)); -- cgit v1.2.3 From d13c11f6f7324b4fe61720910ee54184c38d2fea Mon Sep 17 00:00:00 2001 From: roel kluin <roel.kluin@gmail.com> Date: Mon, 16 Feb 2009 04:02:04 +0000 Subject: sungem: another error printed one too early Another error was printed one too early. Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/sungem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 491876341068..8d64b1da0465 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -1157,7 +1157,7 @@ static void gem_pcs_reset(struct gem *gp) if (limit-- <= 0) break; } - if (limit <= 0) + if (limit < 0) printk(KERN_WARNING "%s: PCS reset bit would not clear.\n", gp->dev->name); } -- cgit v1.2.3 From 9b6d25100ace1dcf9750803ff08f6b61f840be79 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi> Date: Fri, 20 Feb 2009 15:38:45 -0800 Subject: sx.c: fix dbl statement if - add missing braces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caused by 736d54533aed (sx.c: fix missed unlock_kernel() on error path in sx_fw_ioctl()). You guys keep breaking things this way in every single kernel release in at least couple of places... :-( Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi> Acked-by: Dan Carpenter <error27@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/char/sx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/sx.c b/drivers/char/sx.c index f146e90404fa..d7c416566bd9 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1746,9 +1746,10 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd, sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %ld\n", rc); break; case SXIO_DO_RAMTEST: - if (sx_initialized) /* Already initialized: better not ramtest the board. */ + if (sx_initialized) { /* Already initialized: better not ramtest the board. */ rc = -EPERM; break; + } if (IS_SX_BOARD(board)) { rc = do_memtest(board, 0, 0x7000); if (!rc) -- cgit v1.2.3 From b28fe28f2a07ee325834179174a95495d2786561 Mon Sep 17 00:00:00 2001 From: Dan Carpenter <error27@gmail.com> Date: Fri, 20 Feb 2009 15:38:46 -0800 Subject: sx.c: avoid referencing freed memory if copy_from_user() fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "break" would just result in reusing a free'd pointer. I don't have the cards myself to test it though. :/ Signed-off-by: Dan Carpenter <error27@gmail.com> Cc: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/char/sx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/sx.c b/drivers/char/sx.c index d7c416566bd9..518f2a25d91e 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1789,7 +1789,7 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd, nbytes - i : SX_CHUNK_SIZE)) { kfree(tmp); rc = -EFAULT; - break; + goto out; } memcpy_toio(board->base2 + offset + i, tmp, (i + SX_CHUNK_SIZE > nbytes) ? -- cgit v1.2.3 From 3cf311409d37d904335eb720e8a6b2c17bee6698 Mon Sep 17 00:00:00 2001 From: Yang Hongyang <yanghy@cn.fujitsu.com> Date: Fri, 20 Feb 2009 15:38:51 -0800 Subject: atyfb: remove unused local variable `pwr_command' Signed-off-by: Yang Hongyang<yanghy@cn.fujitsu.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/video/aty/aty128fb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index e6e299feb51b..2181ce4d7ebd 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -2365,7 +2365,6 @@ static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx static void aty128_set_suspend(struct aty128fb_par *par, int suspend) { u32 pmgt; - u16 pwr_command; struct pci_dev *pdev = par->pdev; if (!par->pm_reg) -- cgit v1.2.3 From b6adea334c6c89d5e6c94f9196bbf3a279cb53bd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab <mchehab@redhat.com> Date: Fri, 20 Feb 2009 15:38:52 -0800 Subject: 8250: fix boot hang with serial console when using with Serial Over Lan port Intel 8257x Ethernet boards have a feature called Serial Over Lan. This feature works by emulating a serial port, and it is detected by kernel as a normal 8250 port. However, this emulation is not perfect, as also noticed on changeset 7500b1f602aad75901774a67a687ee985d85893f. Before this patch, the kernel were trying to check if the serial TX is capable of work using IRQ's. This were done with a code similar this: serial_outp(up, UART_IER, UART_IER_THRI); lsr = serial_in(up, UART_LSR); iir = serial_in(up, UART_IIR); serial_outp(up, UART_IER, 0); if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) up->bugs |= UART_BUG_TXEN; This works fine for other 8250 ports, but, on 8250-emulated SoL port, the chip is a little lazy to down UART_IIR_NO_INT at UART_IIR register. Due to that, UART_BUG_TXEN is sometimes enabled. However, as TX IRQ keeps working, and the TX polling is now enabled, the driver miss-interprets the IRQ received later, hanging up the machine until a key is pressed at the serial console. This is the 6 version of this patch. Previous versions were trying to introduce a large enough delay between serial_outp and serial_in(up, UART_IIR), but not taking forever. However, the needed delay couldn't be safely determined. At the experimental tests, a delay of 1us solves most of the cases, but still hangs sometimes. Increasing the delay to 5us was better, but still doesn't solve. A very high delay of 50 ms seemed to work every time. However, poking around with delays and pray for it to be enough doesn't seem to be a good approach, even for a quirk. So, instead of playing with random large arbitrary delays, let's just disable UART_BUG_TXEN for all SoL ports. [akpm@linux-foundation.org: fix warnings] Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/serial/8250.c | 15 +++++++++++++++ drivers/serial/8250_pci.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 3 +++ include/linux/serial_core.h | 1 + 4 files changed, 55 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 0d934bfbdd9b..b4b39811b445 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2083,6 +2083,20 @@ static int serial8250_startup(struct uart_port *port) serial8250_set_mctrl(&up->port, up->port.mctrl); + /* Serial over Lan (SoL) hack: + Intel 8257x Gigabit ethernet chips have a + 16550 emulation, to be used for Serial Over Lan. + Those chips take a longer time than a normal + serial device to signalize that a transmission + data was queued. Due to that, the above test generally + fails. One solution would be to delay the reading of + iir. However, this is not reliable, since the timeout + is variable. So, let's just don't test if we receive + TX irq. This way, we'll never enable UART_BUG_TXEN. + */ + if (up->port.flags & UPF_NO_TXEN_TEST) + goto dont_test_tx_en; + /* * Do a quick test to see if we receive an * interrupt when we enable the TX irq. @@ -2102,6 +2116,7 @@ static int serial8250_startup(struct uart_port *port) up->bugs &= ~UART_BUG_TXEN; } +dont_test_tx_en: spin_unlock_irqrestore(&up->port.lock, flags); /* diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 536d8e510f66..533f82025adf 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -798,6 +798,21 @@ pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int skip_tx_en_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_NO_TXEN_TEST; + printk(KERN_DEBUG "serial8250: skipping TxEn test for device " + "[%04x:%04x] subsystem [%04x:%04x]\n", + priv->dev->vendor, + priv->dev->device, + priv->dev->subsystem_vendor, + priv->dev->subsystem_device); + + return pci_default_setup(priv, board, port, idx); +} + /* This should be in linux/pci_ids.h */ #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B @@ -864,6 +879,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .init = pci_inteli960ni_init, .setup = pci_default_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_8257X_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573L_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573E_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, /* * ITE */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 114b8192eab9..aca8c458aa8a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2323,6 +2323,9 @@ #define PCI_DEVICE_ID_INTEL_82378 0x0484 #define PCI_DEVICE_ID_INTEL_I960 0x0960 #define PCI_DEVICE_ID_INTEL_I960RM 0x0962 +#define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062 +#define PCI_DEVICE_ID_INTEL_82573E_SOL 0x1085 +#define PCI_DEVICE_ID_INTEL_82573L_SOL 0x108F #define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 #define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 #define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 90bbbf0b1161..df9245c7bd3b 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -296,6 +296,7 @@ struct uart_port { #define UPF_HARDPPS_CD ((__force upf_t) (1 << 11)) #define UPF_LOW_LATENCY ((__force upf_t) (1 << 13)) #define UPF_BUGGY_UART ((__force upf_t) (1 << 14)) +#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) -- cgit v1.2.3 From 5423a0cb3f74c16e90683f8ee1cec6c240a9556e Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy <astarikovskiy@suse.de> Date: Sat, 21 Feb 2009 12:18:13 -0500 Subject: ACPI: EC: Add delay for slow MSI controller http://bugzilla.kernel.org/show_bug.cgi?id=12011 Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de> Signed-off-by: Len Brown <len.brown@intel.com> --- drivers/acpi/ec.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 5c2f5d343be6..2fe15060dcdc 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -120,6 +120,8 @@ static struct acpi_ec { spinlock_t curr_lock; } *boot_ec, *first_ec; +static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ + /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ @@ -259,6 +261,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); acpi_disable_gpe(NULL, ec->gpe); } + if (EC_FLAGS_MSI) + udelay(ACPI_EC_DELAY); /* start transaction */ spin_lock_irqsave(&ec->curr_lock, tmp); /* following two actions should be kept atomic */ @@ -967,6 +971,11 @@ int __init acpi_ec_ecdt_probe(void) /* * Generate a boot ec context */ + if (dmi_name_in_vendors("Micro-Star") || + dmi_name_in_vendors("Notebook")) { + pr_info(PREFIX "Enabling special treatment for EC from MSI.\n"); + EC_FLAGS_MSI = 1; + } status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); if (ACPI_SUCCESS(status)) { -- cgit v1.2.3 From 56f382a08722186623400180adbb9d1be1721cee Mon Sep 17 00:00:00 2001 From: Richard Hughes <hughsient@gmail.com> Date: Sun, 25 Jan 2009 15:05:50 +0000 Subject: battery: don't assume we are fully charged when not charging or discharging On hardware like the T61 it can take a couple of seconds for the battery to start charging after the power is connected, and we incorrectly tell userspace that we are fully charged, and then go back to charging. Only mark a battery as fully charged when the preset charge matches either the last full charge, or the design charge. http://bugzilla.kernel.org/show_bug.cgi?id=12632 Signed-off-by: Richard Hughes <hughsient@gmail.com> Acked-by: Alexey Starikovskiy <astarikovskiy@suse.de> Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Len Brown <len.brown@intel.com> --- drivers/acpi/battery.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 65132f920459..69cbc57c2d1c 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -138,6 +138,29 @@ static int acpi_battery_technology(struct acpi_battery *battery) static int acpi_battery_get_state(struct acpi_battery *battery); +static int acpi_battery_is_charged(struct acpi_battery *battery) +{ + /* either charging or discharging */ + if (battery->state != 0) + return 0; + + /* battery not reporting charge */ + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || + battery->capacity_now == 0) + return 0; + + /* good batteries update full_charge as the batteries degrade */ + if (battery->full_charge_capacity == battery->capacity_now) + return 1; + + /* fallback to using design values for broken batteries */ + if (battery->design_capacity == battery->capacity_now) + return 1; + + /* we don't do any sort of metric based on percentages */ + return 0; +} + static int acpi_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -155,7 +178,7 @@ static int acpi_battery_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_STATUS_DISCHARGING; else if (battery->state & 0x02) val->intval = POWER_SUPPLY_STATUS_CHARGING; - else if (battery->state == 0) + else if (acpi_battery_is_charged(battery)) val->intval = POWER_SUPPLY_STATUS_FULL; else val->intval = POWER_SUPPLY_STATUS_UNKNOWN; -- cgit v1.2.3 From 216773a787c3c46ef26bf1742c1fdba37d26be45 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven <arjan@linux.intel.com> Date: Sat, 14 Feb 2009 01:59:06 +0100 Subject: Consolidate driver_probe_done() loops into one place there's a few places that currently loop over driver_probe_done(), and I'm about to add another one. This patch abstracts it into a helper to reduce duplication. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Cc: Len Brown <lenb@kernel.org> Acked-by: Greg KH <gregkh@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/base/dd.c | 17 +++++++++++++++++ include/linux/device.h | 2 ++ init/do_mounts.c | 13 +++++++++---- init/do_mounts_md.c | 5 +++-- 4 files changed, 31 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 315bed8d5e7f..135231239103 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -18,9 +18,11 @@ */ #include <linux/device.h> +#include <linux/delay.h> #include <linux/module.h> #include <linux/kthread.h> #include <linux/wait.h> +#include <linux/async.h> #include "base.h" #include "power/power.h" @@ -167,6 +169,21 @@ int driver_probe_done(void) return 0; } +/** + * wait_for_device_probe + * Wait for device probing to be completed. + * + * Note: this function polls at 100 msec intervals. + */ +int wait_for_device_probe(void) +{ + /* wait for the known devices to complete their probing */ + while (driver_probe_done() != 0) + msleep(100); + async_synchronize_full(); + return 0; +} + /** * driver_probe_device - attempt to bind device & driver together * @drv: driver to bind a device to diff --git a/include/linux/device.h b/include/linux/device.h index 45e5b1921fbb..47f343c7bdda 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -147,6 +147,8 @@ extern void put_driver(struct device_driver *drv); extern struct device_driver *driver_find(const char *name, struct bus_type *bus); extern int driver_probe_done(void); +extern int wait_for_device_probe(void); + /* sysfs interface for exporting driver attributes */ diff --git a/init/do_mounts.c b/init/do_mounts.c index 708105e163df..8d4ff5afc1d8 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -370,10 +370,14 @@ void __init prepare_namespace(void) ssleep(root_delay); } - /* wait for the known devices to complete their probing */ - while (driver_probe_done() != 0) - msleep(100); - async_synchronize_full(); + /* + * wait for the known devices to complete their probing + * + * Note: this is a potential source of long boot delays. + * For example, it is not atypical to wait 5 seconds here + * for the touchpad of a laptop to initialize. + */ + wait_for_device_probe(); md_run_setup(); @@ -399,6 +403,7 @@ void __init prepare_namespace(void) while (driver_probe_done() != 0 || (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) msleep(100); + async_synchronize_full(); } is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; diff --git a/init/do_mounts_md.c b/init/do_mounts_md.c index ff95e3192884..9bdddbcb3d6a 100644 --- a/init/do_mounts_md.c +++ b/init/do_mounts_md.c @@ -281,8 +281,9 @@ static void __init autodetect_raid(void) */ printk(KERN_INFO "md: Waiting for all devices to be available before autodetect\n"); printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n"); - while (driver_probe_done() < 0) - msleep(100); + + wait_for_device_probe(); + fd = sys_open("/dev/md0", 0, 0); if (fd >= 0) { sys_ioctl(fd, RAID_AUTORUN, raid_autopart); -- cgit v1.2.3 From 4898c2b2f04051e19f4230683c0f0b15f71af887 Mon Sep 17 00:00:00 2001 From: Tony Vroon <tony@linx.net> Date: Mon, 2 Feb 2009 11:11:10 +0000 Subject: fujitsu-laptop: Use RFKILL support bitmask from firmware Up until now, we polled the rfkill status for every incoming FUJ02E3 ACPI event. It turns out that the firmware has a bitmask which indicates what rfkill-related state it can report. The rfkill_supported bitmask is now used to avoid polling for rfkill at all in the notification handler if there is no support. Also, it is used in the platform device callbacks. As before we register all callbacks and report "unknown" if the firmware does not give us status updates for that particular bit. This was fed through checkpatch.pl and tested on the S6420, S7020 and P8010 platforms. Signed-off-by: Tony Vroon <tony@linx.net> Tested-by: Stephen Gildea <stepheng+linux@gildea.com> Acked-by: Jonathan Woithe <jwoithe@physics.adelaide.edu.au> Signed-off-by: Len Brown <len.brown@intel.com> --- drivers/platform/x86/fujitsu-laptop.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 65dc41540c62..45940f31fe9e 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -166,6 +166,7 @@ struct fujitsu_hotkey_t { struct platform_device *pf_device; struct kfifo *fifo; spinlock_t fifo_lock; + int rfkill_supported; int rfkill_state; int logolamp_registered; int kblamps_registered; @@ -526,7 +527,7 @@ static ssize_t show_lid_state(struct device *dev, struct device_attribute *attr, char *buf) { - if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + if (!(fujitsu_hotkey->rfkill_supported & 0x100)) return sprintf(buf, "unknown\n"); if (fujitsu_hotkey->rfkill_state & 0x100) return sprintf(buf, "open\n"); @@ -538,7 +539,7 @@ static ssize_t show_dock_state(struct device *dev, struct device_attribute *attr, char *buf) { - if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + if (!(fujitsu_hotkey->rfkill_supported & 0x200)) return sprintf(buf, "unknown\n"); if (fujitsu_hotkey->rfkill_state & 0x200) return sprintf(buf, "docked\n"); @@ -550,7 +551,7 @@ static ssize_t show_radios_state(struct device *dev, struct device_attribute *attr, char *buf) { - if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + if (!(fujitsu_hotkey->rfkill_supported & 0x20)) return sprintf(buf, "unknown\n"); if (fujitsu_hotkey->rfkill_state & 0x20) return sprintf(buf, "on\n"); @@ -928,8 +929,17 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) ; /* No action, result is discarded */ vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); - fujitsu_hotkey->rfkill_state = - call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); + fujitsu_hotkey->rfkill_supported = + call_fext_func(FUNC_RFKILL, 0x0, 0x0, 0x0); + + /* Make sure our bitmask of supported functions is cleared if the + RFKILL function block is not implemented, like on the S7020. */ + if (fujitsu_hotkey->rfkill_supported == UNSUPPORTED_CMD) + fujitsu_hotkey->rfkill_supported = 0; + + if (fujitsu_hotkey->rfkill_supported) + fujitsu_hotkey->rfkill_state = + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); /* Suspect this is a keymap of the application panel, print it */ printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", @@ -1005,8 +1015,9 @@ static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, input = fujitsu_hotkey->input; - fujitsu_hotkey->rfkill_state = - call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); + if (fujitsu_hotkey->rfkill_supported) + fujitsu_hotkey->rfkill_state = + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); switch (event) { case ACPI_FUJITSU_NOTIFY_CODE1: -- cgit v1.2.3 From ba193d64abfe644e8752affa310a368eda01f46e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas <bjorn.helgaas@hp.com> Date: Thu, 19 Feb 2009 12:56:16 -0700 Subject: ACPI: remove CONFIG_ACPI_SYSTEM Remove CONFIG_ACPI_SYSTEM. It was always set the same as CONFIG_ACPI, and it had no menu label, so there was no way to set it to anything other than "y". Some things under CONFIG_ACPI_SYSTEM (acpi_irq_handled, acpi_os_gpe_count(), event_is_open, register_acpi_notifier(), etc.) are used unconditionally by the CA, the OSPM, and drivers, so we depend on them always being present. Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Len Brown <len.brown@intel.com> --- drivers/acpi/Kconfig | 7 ------- drivers/acpi/Makefile | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index a7799a99f2d9..8a851d0f4384 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -254,13 +254,6 @@ config ACPI_PCI_SLOT help you correlate PCI bus addresses with the physical geography of your slots. If you are unsure, say N. -config ACPI_SYSTEM - bool - default y - help - This driver will enable your system to shut down using ACPI, and - dump your ACPI DSDT table using /proc/acpi/dsdt. - config X86_PM_TIMER bool "Power Management Timer Support" if EMBEDDED depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 65d90c720b5a..b130ea0d0759 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -52,7 +52,7 @@ obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-$(CONFIG_ACPI_CONTAINER) += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-y += power.o -obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o +obj-y += system.o event.o obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o -- cgit v1.2.3 From b956d41162b1f2c4b446107e9910e4719cbc75f4 Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Sat, 21 Feb 2009 23:46:36 -0800 Subject: sunlance: Beyond ARRAY_SIZE of ib->btx_ring Do not go beyond ARRAY_SIZE of ib->btx_ring Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/sunlance.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 281373281756..16c528db7251 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -343,7 +343,7 @@ static void lance_init_ring_dvma(struct net_device *dev) ib->phys_addr [5] = dev->dev_addr [4]; /* Setup the Tx ring entries */ - for (i = 0; i <= TX_RING_SIZE; i++) { + for (i = 0; i < TX_RING_SIZE; i++) { leptr = LANCE_ADDR(aib + libbuff_offset(tx_buf, i)); ib->btx_ring [i].tmd0 = leptr; ib->btx_ring [i].tmd1_hadr = leptr >> 16; @@ -399,7 +399,7 @@ static void lance_init_ring_pio(struct net_device *dev) sbus_writeb(dev->dev_addr[4], &ib->phys_addr[5]); /* Setup the Tx ring entries */ - for (i = 0; i <= TX_RING_SIZE; i++) { + for (i = 0; i < TX_RING_SIZE; i++) { leptr = libbuff_offset(tx_buf, i); sbus_writew(leptr, &ib->btx_ring [i].tmd0); sbus_writeb(leptr >> 16,&ib->btx_ring [i].tmd1_hadr); -- cgit v1.2.3 From ee923623177249cf22c43419ad0e8ff926dd1f58 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano <daniel.lezcano@free.fr> Date: Sun, 22 Feb 2009 00:04:45 -0800 Subject: veth : add the set_mac_address capability Fix lost set_mac_address capability. Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/veth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 852d0e7c4e62..108bbbeacfb6 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -263,10 +263,11 @@ static void veth_dev_free(struct net_device *dev) } static const struct net_device_ops veth_netdev_ops = { - .ndo_init = veth_dev_init, - .ndo_open = veth_open, - .ndo_start_xmit = veth_xmit, - .ndo_get_stats = veth_get_stats, + .ndo_init = veth_dev_init, + .ndo_open = veth_open, + .ndo_start_xmit = veth_xmit, + .ndo_get_stats = veth_get_stats, + .ndo_set_mac_address = eth_mac_addr, }; static void veth_setup(struct net_device *dev) -- cgit v1.2.3 From 3d92e8f3ae9ba21cac30370eb254ed9dc20df043 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert@linux-m68k.org> Date: Sun, 22 Feb 2009 09:38:47 +0100 Subject: m68k: atari - Rename "mfp" to "st_mfp" http://kisskb.ellerman.id.au/kisskb/buildresult/72115/: | net/mac80211/ieee80211_i.h:327: error: syntax error before 'volatile' | net/mac80211/ieee80211_i.h:350: error: syntax error before '}' token | net/mac80211/ieee80211_i.h:455: error: field 'sta' has incomplete type | distcc[19430] ERROR: compile net/mac80211/main.c on sprygo/32 failed This is caused by | # define mfp ((*(volatile struct MFP*)MFP_BAS)) in arch/m68k/include/asm/atarihw.h, which conflicts with the new "mfp" enum in net/mac80211/ieee80211_i.h. Rename "mfp" to "st_mfp", as it's a way too generic name for a global #define. Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- arch/m68k/atari/ataints.c | 16 ++++++++-------- arch/m68k/atari/atakeyb.c | 4 ++-- arch/m68k/atari/config.c | 2 +- arch/m68k/atari/debug.c | 22 +++++++++++----------- arch/m68k/atari/time.c | 8 ++++---- arch/m68k/include/asm/atarihw.h | 4 ++-- arch/m68k/include/asm/atariints.h | 6 +++--- drivers/block/ataflop.c | 4 ++-- drivers/char/scc.h | 2 +- drivers/parport/parport_atari.c | 6 +++--- drivers/video/atafb.c | 22 +++++++++++----------- sound/oss/dmasound/dmasound_atari.c | 16 ++++++++-------- 12 files changed, 56 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c index dba4afabb444..39478dd08e67 100644 --- a/arch/m68k/atari/ataints.c +++ b/arch/m68k/atari/ataints.c @@ -187,8 +187,8 @@ __asm__ (__ALIGN_STR "\n" \ " jbra ret_from_interrupt\n" \ : : "i" (&kstat_cpu(0).irqs[n+8]), "i" (&irq_handler[n+8]), \ "n" (PT_OFF_SR), "n" (n), \ - "i" (n & 8 ? (n & 16 ? &tt_mfp.int_mk_a : &mfp.int_mk_a) \ - : (n & 16 ? &tt_mfp.int_mk_b : &mfp.int_mk_b)), \ + "i" (n & 8 ? (n & 16 ? &tt_mfp.int_mk_a : &st_mfp.int_mk_a) \ + : (n & 16 ? &tt_mfp.int_mk_b : &st_mfp.int_mk_b)), \ "m" (preempt_count()), "di" (HARDIRQ_OFFSET) \ ); \ for (;;); /* fake noreturn */ \ @@ -366,14 +366,14 @@ void __init atari_init_IRQ(void) /* Initialize the MFP(s) */ #ifdef ATARI_USE_SOFTWARE_EOI - mfp.vec_adr = 0x48; /* Software EOI-Mode */ + st_mfp.vec_adr = 0x48; /* Software EOI-Mode */ #else - mfp.vec_adr = 0x40; /* Automatic EOI-Mode */ + st_mfp.vec_adr = 0x40; /* Automatic EOI-Mode */ #endif - mfp.int_en_a = 0x00; /* turn off MFP-Ints */ - mfp.int_en_b = 0x00; - mfp.int_mk_a = 0xff; /* no Masking */ - mfp.int_mk_b = 0xff; + st_mfp.int_en_a = 0x00; /* turn off MFP-Ints */ + st_mfp.int_en_b = 0x00; + st_mfp.int_mk_a = 0xff; /* no Masking */ + st_mfp.int_mk_b = 0xff; if (ATARIHW_PRESENT(TT_MFP)) { #ifdef ATARI_USE_SOFTWARE_EOI diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index a5f33c059979..4add96d13b19 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -609,10 +609,10 @@ int atari_keyb_init(void) ACIA_RHTID : 0); /* make sure the interrupt line is up */ - } while ((mfp.par_dt_reg & 0x10) == 0); + } while ((st_mfp.par_dt_reg & 0x10) == 0); /* enable ACIA Interrupts */ - mfp.active_edge &= ~0x10; + st_mfp.active_edge &= ~0x10; atari_turnon_irq(IRQ_MFP_ACIA); ikbd_self_test = 1; diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c index 49c28cdbea5c..ae2d96e5d618 100644 --- a/arch/m68k/atari/config.c +++ b/arch/m68k/atari/config.c @@ -258,7 +258,7 @@ void __init config_atari(void) printk("STND_SHIFTER "); } } - if (hwreg_present(&mfp.par_dt_reg)) { + if (hwreg_present(&st_mfp.par_dt_reg)) { ATARIHW_SET(ST_MFP); printk("ST_MFP "); } diff --git a/arch/m68k/atari/debug.c b/arch/m68k/atari/debug.c index 702b15ccfab7..28efdc33c1ae 100644 --- a/arch/m68k/atari/debug.c +++ b/arch/m68k/atari/debug.c @@ -34,9 +34,9 @@ static struct console atari_console_driver = { static inline void ata_mfp_out(char c) { - while (!(mfp.trn_stat & 0x80)) /* wait for tx buf empty */ + while (!(st_mfp.trn_stat & 0x80)) /* wait for tx buf empty */ barrier(); - mfp.usart_dta = c; + st_mfp.usart_dta = c; } static void atari_mfp_console_write(struct console *co, const char *str, @@ -91,7 +91,7 @@ static int ata_par_out(char c) /* This a some-seconds timeout in case no printer is connected */ unsigned long i = loops_per_jiffy > 1 ? loops_per_jiffy : 10000000/HZ; - while ((mfp.par_dt_reg & 1) && --i) /* wait for BUSY == L */ + while ((st_mfp.par_dt_reg & 1) && --i) /* wait for BUSY == L */ ; if (!i) return 0; @@ -131,9 +131,9 @@ static void atari_par_console_write(struct console *co, const char *str, #if 0 int atari_mfp_console_wait_key(struct console *co) { - while (!(mfp.rcv_stat & 0x80)) /* wait for rx buf filled */ + while (!(st_mfp.rcv_stat & 0x80)) /* wait for rx buf filled */ barrier(); - return mfp.usart_dta; + return st_mfp.usart_dta; } int atari_scc_console_wait_key(struct console *co) @@ -175,12 +175,12 @@ static void __init atari_init_mfp_port(int cflag) baud = B9600; /* use default 9600bps for non-implemented rates */ baud -= B1200; /* baud_table[] starts at 1200bps */ - mfp.trn_stat &= ~0x01; /* disable TX */ - mfp.usart_ctr = parity | csize | 0x88; /* 1:16 clk mode, 1 stop bit */ - mfp.tim_ct_cd &= 0x70; /* stop timer D */ - mfp.tim_dt_d = baud_table[baud]; - mfp.tim_ct_cd |= 0x01; /* start timer D, 1:4 */ - mfp.trn_stat |= 0x01; /* enable TX */ + st_mfp.trn_stat &= ~0x01; /* disable TX */ + st_mfp.usart_ctr = parity | csize | 0x88; /* 1:16 clk mode, 1 stop bit */ + st_mfp.tim_ct_cd &= 0x70; /* stop timer D */ + st_mfp.tim_dt_d = baud_table[baud]; + st_mfp.tim_ct_cd |= 0x01; /* start timer D, 1:4 */ + st_mfp.trn_stat |= 0x01; /* enable TX */ } #define SCC_WRITE(reg, val) \ diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c index d076ff8d1b39..a0531f34c617 100644 --- a/arch/m68k/atari/time.c +++ b/arch/m68k/atari/time.c @@ -27,9 +27,9 @@ void __init atari_sched_init(irq_handler_t timer_routine) { /* set Timer C data Register */ - mfp.tim_dt_c = INT_TICKS; + st_mfp.tim_dt_c = INT_TICKS; /* start timer C, div = 1:100 */ - mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60; + st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60; /* install interrupt service routine for MFP Timer C */ if (request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW, "timer", timer_routine)) @@ -46,11 +46,11 @@ unsigned long atari_gettimeoffset (void) unsigned long ticks, offset = 0; /* read MFP timer C current value */ - ticks = mfp.tim_dt_c; + ticks = st_mfp.tim_dt_c; /* The probability of underflow is less than 2% */ if (ticks > INT_TICKS - INT_TICKS / 50) /* Check for pending timer interrupt */ - if (mfp.int_pn_b & (1 << 5)) + if (st_mfp.int_pn_b & (1 << 5)) offset = TICK_SIZE; ticks = INT_TICKS - ticks; diff --git a/arch/m68k/include/asm/atarihw.h b/arch/m68k/include/asm/atarihw.h index 1412b4ab202f..a714e1aa072a 100644 --- a/arch/m68k/include/asm/atarihw.h +++ b/arch/m68k/include/asm/atarihw.h @@ -113,7 +113,7 @@ extern struct atari_hw_present atari_hw_present; * of nops on various machines. Somebody claimed that the tstb takes 600 ns. */ #define MFPDELAY() \ - __asm__ __volatile__ ( "tstb %0" : : "m" (mfp.par_dt_reg) : "cc" ); + __asm__ __volatile__ ( "tstb %0" : : "m" (st_mfp.par_dt_reg) : "cc" ); /* Do cache push/invalidate for DMA read/write. This function obeys the * snooping on some machines (Medusa) and processors: The Medusa itself can @@ -565,7 +565,7 @@ struct MFP u_char char_dummy23; u_char usart_dta; }; -# define mfp ((*(volatile struct MFP*)MFP_BAS)) +# define st_mfp ((*(volatile struct MFP*)MFP_BAS)) /* TT's second MFP */ diff --git a/arch/m68k/include/asm/atariints.h b/arch/m68k/include/asm/atariints.h index 5748e99f4e26..f597892e43a0 100644 --- a/arch/m68k/include/asm/atariints.h +++ b/arch/m68k/include/asm/atariints.h @@ -113,7 +113,7 @@ static inline int get_mfp_bit( unsigned irq, int type ) { unsigned char mask, *reg; mask = 1 << (irq & 7); - reg = (unsigned char *)&mfp.int_en_a + type*4 + + reg = (unsigned char *)&st_mfp.int_en_a + type*4 + ((irq & 8) >> 2) + (((irq-8) & 16) << 3); return( *reg & mask ); } @@ -123,7 +123,7 @@ static inline void set_mfp_bit( unsigned irq, int type ) { unsigned char mask, *reg; mask = 1 << (irq & 7); - reg = (unsigned char *)&mfp.int_en_a + type*4 + + reg = (unsigned char *)&st_mfp.int_en_a + type*4 + ((irq & 8) >> 2) + (((irq-8) & 16) << 3); __asm__ __volatile__ ( "orb %0,%1" : : "di" (mask), "m" (*reg) : "memory" ); @@ -134,7 +134,7 @@ static inline void clear_mfp_bit( unsigned irq, int type ) { unsigned char mask, *reg; mask = ~(1 << (irq & 7)); - reg = (unsigned char *)&mfp.int_en_a + type*4 + + reg = (unsigned char *)&st_mfp.int_en_a + type*4 + ((irq & 8) >> 2) + (((irq-8) & 16) << 3); if (type == MFP_PENDING || type == MFP_SERVICE) __asm__ __volatile__ ( "moveb %0,%1" diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 69e1df7dfa14..4234c11c1e4c 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1730,7 +1730,7 @@ static int __init fd_test_drive_present( int drive ) timeout = jiffies + 2*HZ+HZ/2; while (time_before(jiffies, timeout)) - if (!(mfp.par_dt_reg & 0x20)) + if (!(st_mfp.par_dt_reg & 0x20)) break; status = FDC_READ( FDCREG_STATUS ); @@ -1747,7 +1747,7 @@ static int __init fd_test_drive_present( int drive ) /* dummy seek command to make WP bit accessible */ FDC_WRITE( FDCREG_DATA, 0 ); FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK ); - while( mfp.par_dt_reg & 0x20 ) + while( st_mfp.par_dt_reg & 0x20 ) ; status = FDC_READ( FDCREG_STATUS ); } diff --git a/drivers/char/scc.h b/drivers/char/scc.h index 93998f5baff5..341b1142bea8 100644 --- a/drivers/char/scc.h +++ b/drivers/char/scc.h @@ -387,7 +387,7 @@ struct scc_port { /* The SCC needs 3.5 PCLK cycles recovery time between to register * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 * * 125 ns = 437.5 ns. This is too short for udelay(). - * 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be + * 10/16/95: A tstb st_mfp.par_dt_reg takes 600ns (sure?) and thus should be * quite right */ diff --git a/drivers/parport/parport_atari.c b/drivers/parport/parport_atari.c index ad4cdd256137..0b28fccec03f 100644 --- a/drivers/parport/parport_atari.c +++ b/drivers/parport/parport_atari.c @@ -84,7 +84,7 @@ parport_atari_frob_control(struct parport *p, unsigned char mask, static unsigned char parport_atari_read_status(struct parport *p) { - return ((mfp.par_dt_reg & 1 ? 0 : PARPORT_STATUS_BUSY) | + return ((st_mfp.par_dt_reg & 1 ? 0 : PARPORT_STATUS_BUSY) | PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR); } @@ -193,9 +193,9 @@ static int __init parport_atari_init(void) sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5); local_irq_restore(flags); /* MFP port I0 as input. */ - mfp.data_dir &= ~1; + st_mfp.data_dir &= ~1; /* MFP port I0 interrupt on high->low edge. */ - mfp.active_edge &= ~1; + st_mfp.active_edge &= ~1; p = parport_register_port((unsigned long)&sound_ym.wd_data, IRQ_MFP_BUSY, PARPORT_DMA_NONE, &parport_atari_ops); diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index 8058572a7428..018850c116c6 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -841,7 +841,7 @@ static int tt_detect(void) tt_dmasnd.ctrl = DMASND_CTRL_OFF; udelay(20); /* wait a while for things to settle down */ } - mono_moni = (mfp.par_dt_reg & 0x80) == 0; + mono_moni = (st_mfp.par_dt_reg & 0x80) == 0; tt_get_par(&par); tt_encode_var(&atafb_predefined[0], &par); @@ -2035,7 +2035,7 @@ static int stste_detect(void) tt_dmasnd.ctrl = DMASND_CTRL_OFF; udelay(20); /* wait a while for things to settle down */ } - mono_moni = (mfp.par_dt_reg & 0x80) == 0; + mono_moni = (st_mfp.par_dt_reg & 0x80) == 0; stste_get_par(&par); stste_encode_var(&atafb_predefined[0], &par); @@ -2086,20 +2086,20 @@ static void st_ovsc_switch(void) return; local_irq_save(flags); - mfp.tim_ct_b = 0x10; - mfp.active_edge |= 8; - mfp.tim_ct_b = 0; - mfp.tim_dt_b = 0xf0; - mfp.tim_ct_b = 8; - while (mfp.tim_dt_b > 1) /* TOS does it this way, don't ask why */ + st_mfp.tim_ct_b = 0x10; + st_mfp.active_edge |= 8; + st_mfp.tim_ct_b = 0; + st_mfp.tim_dt_b = 0xf0; + st_mfp.tim_ct_b = 8; + while (st_mfp.tim_dt_b > 1) /* TOS does it this way, don't ask why */ ; - new = mfp.tim_dt_b; + new = st_mfp.tim_dt_b; do { udelay(LINE_DELAY); old = new; - new = mfp.tim_dt_b; + new = st_mfp.tim_dt_b; } while (old != new); - mfp.tim_ct_b = 0x10; + st_mfp.tim_ct_b = 0x10; udelay(SYNC_DELAY); if (atari_switches & ATARI_SWITCH_OVSC_IKBD) diff --git a/sound/oss/dmasound/dmasound_atari.c b/sound/oss/dmasound/dmasound_atari.c index 57d9f154c88b..38931f2f6967 100644 --- a/sound/oss/dmasound/dmasound_atari.c +++ b/sound/oss/dmasound/dmasound_atari.c @@ -847,23 +847,23 @@ static int __init AtaIrqInit(void) of events. So all we need to keep the music playing is to provide the sound hardware with new data upon an interrupt from timer A. */ - mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ - mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ - mfp.tim_ct_a = 8; /* Turn on event counting. */ + st_mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ + st_mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ + st_mfp.tim_ct_a = 8; /* Turn on event counting. */ /* Register interrupt handler. */ if (request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound", AtaInterrupt)) return 0; - mfp.int_en_a |= 0x20; /* Turn interrupt on. */ - mfp.int_mk_a |= 0x20; + st_mfp.int_en_a |= 0x20; /* Turn interrupt on. */ + st_mfp.int_mk_a |= 0x20; return 1; } #ifdef MODULE static void AtaIrqCleanUp(void) { - mfp.tim_ct_a = 0; /* stop timer */ - mfp.int_en_a &= ~0x20; /* turn interrupt off */ + st_mfp.tim_ct_a = 0; /* stop timer */ + st_mfp.int_en_a &= ~0x20; /* turn interrupt off */ free_irq(IRQ_MFP_TIMA, AtaInterrupt); } #endif /* MODULE */ @@ -1599,7 +1599,7 @@ static int __init dmasound_atari_init(void) is_falcon = 0; } else return -ENODEV; - if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0) + if ((st_mfp.int_en_a & st_mfp.int_mk_a & 0x20) == 0) return dmasound_init(); else { printk("DMA sound driver: Timer A interrupt already in use\n"); -- cgit v1.2.3 From 770824bdc421ff58a64db608294323571c949f4c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" <rjw@sisk.pl> Date: Sun, 22 Feb 2009 18:38:50 +0100 Subject: PM: Split up sysdev_[suspend|resume] from device_power_[down|up] Move the sysdev_suspend/resume from the callee to the callers, with no real change in semantics, so that we can rework the disabling of interrupts during suspend/hibernation. This is based on an earlier patch from Linus. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- arch/x86/kernel/apm_32.c | 4 ++++ drivers/base/base.h | 2 -- drivers/base/power/main.c | 3 --- drivers/xen/manage.c | 8 ++++++++ include/linux/pm.h | 2 ++ kernel/kexec.c | 7 +++++++ kernel/power/disk.c | 11 +++++++++++ kernel/power/main.c | 8 ++++++-- 8 files changed, 38 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index 98807bb095ad..266ec6c18b6c 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -1192,6 +1192,7 @@ static int suspend(int vetoable) device_suspend(PMSG_SUSPEND); local_irq_disable(); device_power_down(PMSG_SUSPEND); + sysdev_suspend(PMSG_SUSPEND); local_irq_enable(); @@ -1208,6 +1209,7 @@ static int suspend(int vetoable) if (err != APM_SUCCESS) apm_error("suspend", err); err = (err == APM_SUCCESS) ? 0 : -EIO; + sysdev_resume(); device_power_up(PMSG_RESUME); local_irq_enable(); device_resume(PMSG_RESUME); @@ -1228,6 +1230,7 @@ static void standby(void) local_irq_disable(); device_power_down(PMSG_SUSPEND); + sysdev_suspend(PMSG_SUSPEND); local_irq_enable(); err = set_system_power_state(APM_STATE_STANDBY); @@ -1235,6 +1238,7 @@ static void standby(void) apm_error("standby", err); local_irq_disable(); + sysdev_resume(); device_power_up(PMSG_RESUME); local_irq_enable(); } diff --git a/drivers/base/base.h b/drivers/base/base.h index 0a5f055dffba..9f50f1b545dc 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -88,8 +88,6 @@ extern void driver_detach(struct device_driver *drv); extern int driver_probe_device(struct device_driver *drv, struct device *dev); extern void sysdev_shutdown(void); -extern int sysdev_suspend(pm_message_t state); -extern int sysdev_resume(void); extern char *make_class_name(const char *name, struct kobject *kobj); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 670c9d6c1407..2d14f4ae6c01 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -333,7 +333,6 @@ static void dpm_power_up(pm_message_t state) */ void device_power_up(pm_message_t state) { - sysdev_resume(); dpm_power_up(state); } EXPORT_SYMBOL_GPL(device_power_up); @@ -577,8 +576,6 @@ int device_power_down(pm_message_t state) } dev->power.status = DPM_OFF_IRQ; } - if (!error) - error = sysdev_suspend(state); if (error) dpm_power_up(resume_event(state)); return error; diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 9b91617b9582..56892a142ee2 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -45,6 +45,13 @@ static int xen_suspend(void *data) err); return err; } + err = sysdev_suspend(PMSG_SUSPEND); + if (err) { + printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n", + err); + device_power_up(PMSG_RESUME); + return err; + } xen_mm_pin_all(); gnttab_suspend(); @@ -61,6 +68,7 @@ static int xen_suspend(void *data) gnttab_resume(); xen_mm_unpin_all(); + sysdev_resume(); device_power_up(PMSG_RESUME); if (!*cancelled) { diff --git a/include/linux/pm.h b/include/linux/pm.h index de2e0a8f6728..24ba5f67b3a3 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -381,10 +381,12 @@ struct dev_pm_info { #ifdef CONFIG_PM_SLEEP extern void device_pm_lock(void); +extern int sysdev_resume(void); extern void device_power_up(pm_message_t state); extern void device_resume(pm_message_t state); extern void device_pm_unlock(void); +extern int sysdev_suspend(pm_message_t state); extern int device_power_down(pm_message_t state); extern int device_suspend(pm_message_t state); extern int device_prepare_suspend(pm_message_t state); diff --git a/kernel/kexec.c b/kernel/kexec.c index 8a6d7b08864e..483899578259 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1465,6 +1465,11 @@ int kernel_kexec(void) error = device_power_down(PMSG_FREEZE); if (error) goto Enable_irqs; + + /* Suspend system devices */ + error = sysdev_suspend(PMSG_FREEZE); + if (error) + goto Power_up_devices; } else #endif { @@ -1477,6 +1482,8 @@ int kernel_kexec(void) #ifdef CONFIG_KEXEC_JUMP if (kexec_image->preserve_context) { + sysdev_resume(); + Power_up_devices: device_power_up(PMSG_RESTORE); Enable_irqs: local_irq_enable(); diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 7b40e94b1d42..4a4a206b1979 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -227,6 +227,12 @@ static int create_image(int platform_mode) "aborting hibernation\n"); goto Enable_irqs; } + sysdev_suspend(PMSG_FREEZE); + if (error) { + printk(KERN_ERR "PM: Some devices failed to power down, " + "aborting hibernation\n"); + goto Power_up_devices; + } if (hibernation_test(TEST_CORE)) goto Power_up; @@ -242,9 +248,11 @@ static int create_image(int platform_mode) if (!in_suspend) platform_leave(platform_mode); Power_up: + sysdev_resume(); /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ + Power_up_devices: device_power_up(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); Enable_irqs: @@ -335,6 +343,7 @@ static int resume_target_kernel(void) "aborting resume\n"); goto Enable_irqs; } + sysdev_suspend(PMSG_QUIESCE); /* We'll ignore saved state, but this gets preempt count (etc) right */ save_processor_state(); error = restore_highmem(); @@ -357,6 +366,7 @@ static int resume_target_kernel(void) swsusp_free(); restore_processor_state(); touch_softlockup_watchdog(); + sysdev_resume(); device_power_up(PMSG_RECOVER); Enable_irqs: local_irq_enable(); @@ -440,6 +450,7 @@ int hibernation_platform_enter(void) local_irq_disable(); error = device_power_down(PMSG_HIBERNATE); if (!error) { + sysdev_suspend(PMSG_HIBERNATE); hibernation_ops->enter(); /* We should never get here */ while (1); diff --git a/kernel/power/main.c b/kernel/power/main.c index b4d219016b6c..c9632f841f64 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -298,8 +298,12 @@ static int suspend_enter(suspend_state_t state) goto Done; } - if (!suspend_test(TEST_CORE)) - error = suspend_ops->enter(state); + error = sysdev_suspend(PMSG_SUSPEND); + if (!error) { + if (!suspend_test(TEST_CORE)) + error = suspend_ops->enter(state); + sysdev_resume(); + } device_power_up(PMSG_RESUME); Done: -- cgit v1.2.3 From 0f99fed4606dcbcbe813df831a39fd8f9653ef54 Mon Sep 17 00:00:00 2001 From: Ingo Molnar <mingo@elte.hu> Date: Sun, 22 Feb 2009 22:07:03 +0100 Subject: PM: Split up sysdev_[suspend|resume] from device_power_[down|up], fix Impact: module build fix Fix: ERROR: "sysdev_resume" [arch/x86/kernel/apm.ko] undefined! ERROR: "sysdev_suspend" [arch/x86/kernel/apm.ko] undefined! As these APIs are now used by the APM driver, which can be built as a module. Also fix a few extra (and inconsistent) newlines in comment blocks preceding these functions. Signed-off-by: Ingo Molnar <mingo@elte.hu> --- drivers/base/sys.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/base/sys.c b/drivers/base/sys.c index c98c31ec2f75..b428c8c4bc64 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -303,7 +303,6 @@ void sysdev_unregister(struct sys_device * sysdev) * is guaranteed by virtue of the fact that child devices are registered * after their parents. */ - void sysdev_shutdown(void) { struct sysdev_class * cls; @@ -363,7 +362,6 @@ static void __sysdev_resume(struct sys_device *dev) * This is only called by the device PM core, so we let them handle * all synchronization. */ - int sysdev_suspend(pm_message_t state) { struct sysdev_class * cls; @@ -432,7 +430,7 @@ aux_driver: } return ret; } - +EXPORT_SYMBOL_GPL(sysdev_suspend); /** * sysdev_resume - Bring system devices back to life. @@ -442,7 +440,6 @@ aux_driver: * * Note: Interrupts are disabled when called. */ - int sysdev_resume(void) { struct sysdev_class * cls; @@ -463,7 +460,7 @@ int sysdev_resume(void) } return 0; } - +EXPORT_SYMBOL_GPL(sysdev_resume); int __init system_bus_init(void) { -- cgit v1.2.3 From 965c7ecaf2e2b083d711a01ab33735a4bdeee1a4 Mon Sep 17 00:00:00 2001 From: Ingo Molnar <mingo@elte.hu> Date: Sun, 22 Feb 2009 23:19:12 +0100 Subject: x86: remove the Voyager 32-bit subarch Impact: remove unused/broken code The Voyager subarch last built successfully on the v2.6.26 kernel and has been stale since then and does not build on the v2.6.27, v2.6.28 and v2.6.29-rc5 kernels. No actual users beyond the maintainer reported this breakage. Patches were sent and most of the fixes were accepted but the discussion around how to do a few remaining issues cleanly fizzled out with no resolution and the code remained broken. In the v2.6.30 x86 tree development cycle 32-bit subarch support has been reworked and removed - and the Voyager code, beyond the build problems already known, needs serious and significant changes and probably a rewrite to support it. CONFIG_X86_VOYAGER has been marked BROKEN then. The maintainer has been notified but no patches have been sent so far to fix it. While all other subarchs have been converted to the new scheme, voyager is still broken. We'd prefer to receive patches which clean up the current situation in a constructive way, but even in case of removal there is no obstacle to add that support back after the issues have been sorted out in a mutually acceptable fashion. So remove this inactive code for now. Signed-off-by: Ingo Molnar <mingo@elte.hu> --- arch/x86/Kconfig | 14 - arch/x86/boot/Makefile | 1 - arch/x86/boot/a20.c | 6 - arch/x86/boot/boot.h | 3 - arch/x86/boot/main.c | 5 - arch/x86/boot/voyager.c | 40 - arch/x86/configs/i386_defconfig | 1 - arch/x86/configs/x86_64_defconfig | 1 - arch/x86/include/asm/mach-voyager/do_timer.h | 17 - arch/x86/include/asm/mach-voyager/entry_arch.h | 26 - arch/x86/include/asm/mach-voyager/setup_arch.h | 12 - arch/x86/include/asm/vic.h | 61 - arch/x86/include/asm/voyager.h | 571 -------- arch/x86/lguest/Kconfig | 1 - arch/x86/mach-voyager/Makefile | 8 - arch/x86/mach-voyager/setup.c | 119 -- arch/x86/mach-voyager/voyager_basic.c | 317 ----- arch/x86/mach-voyager/voyager_cat.c | 1197 ---------------- arch/x86/mach-voyager/voyager_smp.c | 1805 ------------------------ arch/x86/mach-voyager/voyager_thread.c | 128 -- arch/x86/xen/Kconfig | 2 +- drivers/lguest/Kconfig | 2 +- 22 files changed, 2 insertions(+), 4335 deletions(-) delete mode 100644 arch/x86/boot/voyager.c delete mode 100644 arch/x86/include/asm/mach-voyager/do_timer.h delete mode 100644 arch/x86/include/asm/mach-voyager/entry_arch.h delete mode 100644 arch/x86/include/asm/mach-voyager/setup_arch.h delete mode 100644 arch/x86/include/asm/vic.h delete mode 100644 arch/x86/include/asm/voyager.h delete mode 100644 arch/x86/mach-voyager/Makefile delete mode 100644 arch/x86/mach-voyager/setup.c delete mode 100644 arch/x86/mach-voyager/voyager_basic.c delete mode 100644 arch/x86/mach-voyager/voyager_cat.c delete mode 100644 arch/x86/mach-voyager/voyager_smp.c delete mode 100644 arch/x86/mach-voyager/voyager_thread.c (limited to 'drivers') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 35efba546e03..5e2919c0ff92 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -302,7 +302,6 @@ config X86_EXTENDED_PLATFORM SGI 320/540 (Visual Workstation) Summit/EXA (IBM x440) Unisys ES7000 IA32 series - Voyager (NCR) If you have one of these systems, or if you want to build a generic distribution kernel, say Y here - otherwise say N. @@ -423,19 +422,6 @@ config X86_ES7000 Support for Unisys ES7000 systems. Say 'Y' here if this kernel is supposed to run on an IA32-based Unisys ES7000 system. -config X86_VOYAGER - bool "Voyager (NCR)" - depends on SMP && !PCI && BROKEN - depends on X86_32_NON_STANDARD - ---help--- - Voyager is an MCA-based 32-way capable SMP architecture proprietary - to NCR Corp. Machine classes 345x/35xx/4100/51xx are Voyager-based. - - *** WARNING *** - - If you do not specifically know you have a Voyager based machine, - say N here, otherwise the kernel you build will not be bootable. - config SCHED_OMIT_FRAME_POINTER def_bool y prompt "Single-depth WCHAN output" diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index cd48c7210016..c70eff69a1fb 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -32,7 +32,6 @@ setup-y += a20.o cmdline.o copy.o cpu.o cpucheck.o edd.o setup-y += header.o main.o mca.o memory.o pm.o pmjump.o setup-y += printf.o string.o tty.o video.o video-mode.o version.o setup-$(CONFIG_X86_APM_BOOT) += apm.o -setup-$(CONFIG_X86_VOYAGER) += voyager.o # The link order of the video-*.o modules can matter. In particular, # video-vga.o *must* be listed first, followed by video-vesa.o. diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c index fba8e9c6a504..7c19ce8c2442 100644 --- a/arch/x86/boot/a20.c +++ b/arch/x86/boot/a20.c @@ -126,11 +126,6 @@ static void enable_a20_fast(void) int enable_a20(void) { -#ifdef CONFIG_X86_VOYAGER - /* On Voyager, a20_test() is unsafe? */ - enable_a20_kbc(); - return 0; -#else int loops = A20_ENABLE_LOOPS; int kbc_err; @@ -164,5 +159,4 @@ int enable_a20(void) } return -1; -#endif } diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h index cc0ef13fba7a..7b2692e897e5 100644 --- a/arch/x86/boot/boot.h +++ b/arch/x86/boot/boot.h @@ -302,9 +302,6 @@ void probe_cards(int unsafe); /* video-vesa.c */ void vesa_store_edid(void); -/* voyager.c */ -int query_voyager(void); - #endif /* __ASSEMBLY__ */ #endif /* BOOT_BOOT_H */ diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c index 197421db1af1..58f0415d3ae0 100644 --- a/arch/x86/boot/main.c +++ b/arch/x86/boot/main.c @@ -149,11 +149,6 @@ void main(void) /* Query MCA information */ query_mca(); - /* Voyager */ -#ifdef CONFIG_X86_VOYAGER - query_voyager(); -#endif - /* Query Intel SpeedStep (IST) information */ query_ist(); diff --git a/arch/x86/boot/voyager.c b/arch/x86/boot/voyager.c deleted file mode 100644 index 433909d61e5c..000000000000 --- a/arch/x86/boot/voyager.c +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright 2007 rPath, Inc. - All Rights Reserved - * - * This file is part of the Linux kernel, and is made available under - * the terms of the GNU General Public License version 2. - * - * ----------------------------------------------------------------------- */ - -/* - * Get the Voyager config information - */ - -#include "boot.h" - -int query_voyager(void) -{ - u8 err; - u16 es, di; - /* Abuse the apm_bios_info area for this */ - u8 *data_ptr = (u8 *)&boot_params.apm_bios_info; - - data_ptr[0] = 0xff; /* Flag on config not found(?) */ - - asm("pushw %%es ; " - "int $0x15 ; " - "setc %0 ; " - "movw %%es, %1 ; " - "popw %%es" - : "=q" (err), "=r" (es), "=D" (di) - : "a" (0xffc0)); - - if (err) - return -1; /* Not Voyager */ - - set_fs(es); - copy_from_fs(data_ptr, di, 7); /* Table is 7 bytes apparently */ - return 0; -} diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index 096dd5359cd9..5c023f6f652c 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -197,7 +197,6 @@ CONFIG_SPARSE_IRQ=y CONFIG_X86_FIND_SMP_CONFIG=y CONFIG_X86_MPPARSE=y # CONFIG_X86_ELAN is not set -# CONFIG_X86_VOYAGER is not set # CONFIG_X86_GENERICARCH is not set # CONFIG_X86_VSMP is not set # CONFIG_X86_RDC321X is not set diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index 2efb5d5063ff..4157cc4a2bde 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -199,7 +199,6 @@ CONFIG_SPARSE_IRQ=y CONFIG_X86_FIND_SMP_CONFIG=y CONFIG_X86_MPPARSE=y # CONFIG_X86_ELAN is not set -# CONFIG_X86_VOYAGER is not set # CONFIG_X86_GENERICARCH is not set # CONFIG_X86_VSMP is not set CONFIG_SCHED_OMIT_FRAME_POINTER=y diff --git a/arch/x86/include/asm/mach-voyager/do_timer.h b/arch/x86/include/asm/mach-voyager/do_timer.h deleted file mode 100644 index 9e5a459fd15b..000000000000 --- a/arch/x86/include/asm/mach-voyager/do_timer.h +++ /dev/null @@ -1,17 +0,0 @@ -/* defines for inline arch setup functions */ -#include <linux/clockchips.h> - -#include <asm/voyager.h> -#include <asm/i8253.h> - -/** - * do_timer_interrupt_hook - hook into timer tick - * - * Call the pit clock event handler. see asm/i8253.h - **/ -static inline void do_timer_interrupt_hook(void) -{ - global_clock_event->event_handler(global_clock_event); - voyager_timer_interrupt(); -} - diff --git a/arch/x86/include/asm/mach-voyager/entry_arch.h b/arch/x86/include/asm/mach-voyager/entry_arch.h deleted file mode 100644 index ae52624b5937..000000000000 --- a/arch/x86/include/asm/mach-voyager/entry_arch.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8 -*- */ - -/* Copyright (C) 2002 - * - * Author: James.Bottomley@HansenPartnership.com - * - * linux/arch/i386/voyager/entry_arch.h - * - * This file builds the VIC and QIC CPI gates - */ - -/* initialise the voyager interrupt gates - * - * This uses the macros in irq.h to set up assembly jump gates. The - * calls are then redirected to the same routine with smp_ prefixed */ -BUILD_INTERRUPT(vic_sys_interrupt, VIC_SYS_INT) -BUILD_INTERRUPT(vic_cmn_interrupt, VIC_CMN_INT) -BUILD_INTERRUPT(vic_cpi_interrupt, VIC_CPI_LEVEL0); - -/* do all the QIC interrupts */ -BUILD_INTERRUPT(qic_timer_interrupt, QIC_TIMER_CPI); -BUILD_INTERRUPT(qic_invalidate_interrupt, QIC_INVALIDATE_CPI); -BUILD_INTERRUPT(qic_reschedule_interrupt, QIC_RESCHEDULE_CPI); -BUILD_INTERRUPT(qic_enable_irq_interrupt, QIC_ENABLE_IRQ_CPI); -BUILD_INTERRUPT(qic_call_function_interrupt, QIC_CALL_FUNCTION_CPI); -BUILD_INTERRUPT(qic_call_function_single_interrupt, QIC_CALL_FUNCTION_SINGLE_CPI); diff --git a/arch/x86/include/asm/mach-voyager/setup_arch.h b/arch/x86/include/asm/mach-voyager/setup_arch.h deleted file mode 100644 index 71729ca05cd7..000000000000 --- a/arch/x86/include/asm/mach-voyager/setup_arch.h +++ /dev/null @@ -1,12 +0,0 @@ -#include <asm/voyager.h> -#include <asm/setup.h> -#define VOYAGER_BIOS_INFO ((struct voyager_bios_info *) \ - (&boot_params.apm_bios_info)) - -/* Hook to call BIOS initialisation function */ - -/* for voyager, pass the voyager BIOS/SUS info area to the detection - * routines */ - -#define ARCH_SETUP voyager_detect(VOYAGER_BIOS_INFO); - diff --git a/arch/x86/include/asm/vic.h b/arch/x86/include/asm/vic.h deleted file mode 100644 index 53100f353612..000000000000 --- a/arch/x86/include/asm/vic.h +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright (C) 1999,2001 - * - * Author: J.E.J.Bottomley@HansenPartnership.com - * - * Standard include definitions for the NCR Voyager Interrupt Controller */ - -/* The eight CPI vectors. To activate a CPI, you write a bit mask - * corresponding to the processor set to be interrupted into the - * relevant register. That set of CPUs will then be interrupted with - * the CPI */ -static const int VIC_CPI_Registers[] = - {0xFC00, 0xFC01, 0xFC08, 0xFC09, - 0xFC10, 0xFC11, 0xFC18, 0xFC19 }; - -#define VIC_PROC_WHO_AM_I 0xfc29 -# define QUAD_IDENTIFIER 0xC0 -# define EIGHT_SLOT_IDENTIFIER 0xE0 -#define QIC_EXTENDED_PROCESSOR_SELECT 0xFC72 -#define VIC_CPI_BASE_REGISTER 0xFC41 -#define VIC_PROCESSOR_ID 0xFC21 -# define VIC_CPU_MASQUERADE_ENABLE 0x8 - -#define VIC_CLAIM_REGISTER_0 0xFC38 -#define VIC_CLAIM_REGISTER_1 0xFC39 -#define VIC_REDIRECT_REGISTER_0 0xFC60 -#define VIC_REDIRECT_REGISTER_1 0xFC61 -#define VIC_PRIORITY_REGISTER 0xFC20 - -#define VIC_PRIMARY_MC_BASE 0xFC48 -#define VIC_SECONDARY_MC_BASE 0xFC49 - -#define QIC_PROCESSOR_ID 0xFC71 -# define QIC_CPUID_ENABLE 0x08 - -#define QIC_VIC_CPI_BASE_REGISTER 0xFC79 -#define QIC_CPI_BASE_REGISTER 0xFC7A - -#define QIC_MASK_REGISTER0 0xFC80 -/* NOTE: these are masked high, enabled low */ -# define QIC_PERF_TIMER 0x01 -# define QIC_LPE 0x02 -# define QIC_SYS_INT 0x04 -# define QIC_CMN_INT 0x08 -/* at the moment, just enable CMN_INT, disable SYS_INT */ -# define QIC_DEFAULT_MASK0 (~(QIC_CMN_INT /* | VIC_SYS_INT */)) -#define QIC_MASK_REGISTER1 0xFC81 -# define QIC_BOOT_CPI_MASK 0xFE -/* Enable CPI's 1-6 inclusive */ -# define QIC_CPI_ENABLE 0x81 - -#define QIC_INTERRUPT_CLEAR0 0xFC8A -#define QIC_INTERRUPT_CLEAR1 0xFC8B - -/* this is where we place the CPI vectors */ -#define VIC_DEFAULT_CPI_BASE 0xC0 -/* this is where we place the QIC CPI vectors */ -#define QIC_DEFAULT_CPI_BASE 0xD0 - -#define VIC_BOOT_INTERRUPT_MASK 0xfe - -extern void smp_vic_timer_interrupt(void); diff --git a/arch/x86/include/asm/voyager.h b/arch/x86/include/asm/voyager.h deleted file mode 100644 index c1635d43616f..000000000000 --- a/arch/x86/include/asm/voyager.h +++ /dev/null @@ -1,571 +0,0 @@ -/* Copyright (C) 1999,2001 - * - * Author: J.E.J.Bottomley@HansenPartnership.com - * - * Standard include definitions for the NCR Voyager system */ - -#undef VOYAGER_DEBUG -#undef VOYAGER_CAT_DEBUG - -#ifdef VOYAGER_DEBUG -#define VDEBUG(x) printk x -#else -#define VDEBUG(x) -#endif - -/* There are three levels of voyager machine: 3,4 and 5. The rule is - * if it's less than 3435 it's a Level 3 except for a 3360 which is - * a level 4. A 3435 or above is a Level 5 */ -#define VOYAGER_LEVEL5_AND_ABOVE 0x3435 -#define VOYAGER_LEVEL4 0x3360 - -/* The L4 DINO ASIC */ -#define VOYAGER_DINO 0x43 - -/* voyager ports in standard I/O space */ -#define VOYAGER_MC_SETUP 0x96 - - -#define VOYAGER_CAT_CONFIG_PORT 0x97 -# define VOYAGER_CAT_DESELECT 0xff -#define VOYAGER_SSPB_RELOCATION_PORT 0x98 - -/* Valid CAT controller commands */ -/* start instruction register cycle */ -#define VOYAGER_CAT_IRCYC 0x01 -/* start data register cycle */ -#define VOYAGER_CAT_DRCYC 0x02 -/* move to execute state */ -#define VOYAGER_CAT_RUN 0x0F -/* end operation */ -#define VOYAGER_CAT_END 0x80 -/* hold in idle state */ -#define VOYAGER_CAT_HOLD 0x90 -/* single step an "intest" vector */ -#define VOYAGER_CAT_STEP 0xE0 -/* return cat controller to CLEMSON mode */ -#define VOYAGER_CAT_CLEMSON 0xFF - -/* the default cat command header */ -#define VOYAGER_CAT_HEADER 0x7F - -/* the range of possible CAT module ids in the system */ -#define VOYAGER_MIN_MODULE 0x10 -#define VOYAGER_MAX_MODULE 0x1f - -/* The voyager registers per asic */ -#define VOYAGER_ASIC_ID_REG 0x00 -#define VOYAGER_ASIC_TYPE_REG 0x01 -/* the sub address registers can be made auto incrementing on reads */ -#define VOYAGER_AUTO_INC_REG 0x02 -# define VOYAGER_AUTO_INC 0x04 -# define VOYAGER_NO_AUTO_INC 0xfb -#define VOYAGER_SUBADDRDATA 0x03 -#define VOYAGER_SCANPATH 0x05 -# define VOYAGER_CONNECT_ASIC 0x01 -# define VOYAGER_DISCONNECT_ASIC 0xfe -#define VOYAGER_SUBADDRLO 0x06 -#define VOYAGER_SUBADDRHI 0x07 -#define VOYAGER_SUBMODSELECT 0x08 -#define VOYAGER_SUBMODPRESENT 0x09 - -#define VOYAGER_SUBADDR_LO 0xff -#define VOYAGER_SUBADDR_HI 0xffff - -/* the maximum size of a scan path -- used to form instructions */ -#define VOYAGER_MAX_SCAN_PATH 0x100 -/* the biggest possible register size (in bytes) */ -#define VOYAGER_MAX_REG_SIZE 4 - -/* Total number of possible modules (including submodules) */ -#define VOYAGER_MAX_MODULES 16 -/* Largest number of asics per module */ -#define VOYAGER_MAX_ASICS_PER_MODULE 7 - -/* the CAT asic of each module is always the first one */ -#define VOYAGER_CAT_ID 0 -#define VOYAGER_PSI 0x1a - -/* voyager instruction operations and registers */ -#define VOYAGER_READ_CONFIG 0x1 -#define VOYAGER_WRITE_CONFIG 0x2 -#define VOYAGER_BYPASS 0xff - -typedef struct voyager_asic { - __u8 asic_addr; /* ASIC address; Level 4 */ - __u8 asic_type; /* ASIC type */ - __u8 asic_id; /* ASIC id */ - __u8 jtag_id[4]; /* JTAG id */ - __u8 asic_location; /* Location within scan path; start w/ 0 */ - __u8 bit_location; /* Location within bit stream; start w/ 0 */ - __u8 ireg_length; /* Instruction register length */ - __u16 subaddr; /* Amount of sub address space */ - struct voyager_asic *next; /* Next asic in linked list */ -} voyager_asic_t; - -typedef struct voyager_module { - __u8 module_addr; /* Module address */ - __u8 scan_path_connected; /* Scan path connected */ - __u16 ee_size; /* Size of the EEPROM */ - __u16 num_asics; /* Number of Asics */ - __u16 inst_bits; /* Instruction bits in the scan path */ - __u16 largest_reg; /* Largest register in the scan path */ - __u16 smallest_reg; /* Smallest register in the scan path */ - voyager_asic_t *asic; /* First ASIC in scan path (CAT_I) */ - struct voyager_module *submodule; /* Submodule pointer */ - struct voyager_module *next; /* Next module in linked list */ -} voyager_module_t; - -typedef struct voyager_eeprom_hdr { - __u8 module_id[4]; - __u8 version_id; - __u8 config_id; - __u16 boundry_id; /* boundary scan id */ - __u16 ee_size; /* size of EEPROM */ - __u8 assembly[11]; /* assembly # */ - __u8 assembly_rev; /* assembly rev */ - __u8 tracer[4]; /* tracer number */ - __u16 assembly_cksum; /* asm checksum */ - __u16 power_consump; /* pwr requirements */ - __u16 num_asics; /* number of asics */ - __u16 bist_time; /* min. bist time */ - __u16 err_log_offset; /* error log offset */ - __u16 scan_path_offset;/* scan path offset */ - __u16 cct_offset; - __u16 log_length; /* length of err log */ - __u16 xsum_end; /* offset to end of - checksum */ - __u8 reserved[4]; - __u8 sflag; /* starting sentinal */ - __u8 part_number[13]; /* prom part number */ - __u8 version[10]; /* version number */ - __u8 signature[8]; - __u16 eeprom_chksum; - __u32 data_stamp_offset; - __u8 eflag ; /* ending sentinal */ -} __attribute__((packed)) voyager_eprom_hdr_t; - - - -#define VOYAGER_EPROM_SIZE_OFFSET \ - ((__u16)(&(((voyager_eprom_hdr_t *)0)->ee_size))) -#define VOYAGER_XSUM_END_OFFSET 0x2a - -/* the following three definitions are for internal table layouts - * in the module EPROMs. We really only care about the IDs and - * offsets */ -typedef struct voyager_sp_table { - __u8 asic_id; - __u8 bypass_flag; - __u16 asic_data_offset; - __u16 config_data_offset; -} __attribute__((packed)) voyager_sp_table_t; - -typedef struct voyager_jtag_table { - __u8 icode[4]; - __u8 runbist[4]; - __u8 intest[4]; - __u8 samp_preld[4]; - __u8 ireg_len; -} __attribute__((packed)) voyager_jtt_t; - -typedef struct voyager_asic_data_table { - __u8 jtag_id[4]; - __u16 length_bsr; - __u16 length_bist_reg; - __u32 bist_clk; - __u16 subaddr_bits; - __u16 seed_bits; - __u16 sig_bits; - __u16 jtag_offset; -} __attribute__((packed)) voyager_at_t; - -/* Voyager Interrupt Controller (VIC) registers */ - -/* Base to add to Cross Processor Interrupts (CPIs) when triggering - * the CPU IRQ line */ -/* register defines for the WCBICs (one per processor) */ -#define VOYAGER_WCBIC0 0x41 /* bus A node P1 processor 0 */ -#define VOYAGER_WCBIC1 0x49 /* bus A node P1 processor 1 */ -#define VOYAGER_WCBIC2 0x51 /* bus A node P2 processor 0 */ -#define VOYAGER_WCBIC3 0x59 /* bus A node P2 processor 1 */ -#define VOYAGER_WCBIC4 0x61 /* bus B node P1 processor 0 */ -#define VOYAGER_WCBIC5 0x69 /* bus B node P1 processor 1 */ -#define VOYAGER_WCBIC6 0x71 /* bus B node P2 processor 0 */ -#define VOYAGER_WCBIC7 0x79 /* bus B node P2 processor 1 */ - - -/* top of memory registers */ -#define VOYAGER_WCBIC_TOM_L 0x4 -#define VOYAGER_WCBIC_TOM_H 0x5 - -/* register defines for Voyager Memory Contol (VMC) - * these are present on L4 machines only */ -#define VOYAGER_VMC1 0x81 -#define VOYAGER_VMC2 0x91 -#define VOYAGER_VMC3 0xa1 -#define VOYAGER_VMC4 0xb1 - -/* VMC Ports */ -#define VOYAGER_VMC_MEMORY_SETUP 0x9 -# define VMC_Interleaving 0x01 -# define VMC_4Way 0x02 -# define VMC_EvenCacheLines 0x04 -# define VMC_HighLine 0x08 -# define VMC_Start0_Enable 0x20 -# define VMC_Start1_Enable 0x40 -# define VMC_Vremap 0x80 -#define VOYAGER_VMC_BANK_DENSITY 0xa -# define VMC_BANK_EMPTY 0 -# define VMC_BANK_4MB 1 -# define VMC_BANK_16MB 2 -# define VMC_BANK_64MB 3 -# define VMC_BANK0_MASK 0x03 -# define VMC_BANK1_MASK 0x0C -# define VMC_BANK2_MASK 0x30 -# define VMC_BANK3_MASK 0xC0 - -/* Magellan Memory Controller (MMC) defines - present on L5 */ -#define VOYAGER_MMC_ASIC_ID 1 -/* the two memory modules corresponding to memory cards in the system */ -#define VOYAGER_MMC_MEMORY0_MODULE 0x14 -#define VOYAGER_MMC_MEMORY1_MODULE 0x15 -/* the Magellan Memory Address (MMA) defines */ -#define VOYAGER_MMA_ASIC_ID 2 - -/* Submodule number for the Quad Baseboard */ -#define VOYAGER_QUAD_BASEBOARD 1 - -/* ASIC defines for the Quad Baseboard */ -#define VOYAGER_QUAD_QDATA0 1 -#define VOYAGER_QUAD_QDATA1 2 -#define VOYAGER_QUAD_QABC 3 - -/* Useful areas in extended CMOS */ -#define VOYAGER_PROCESSOR_PRESENT_MASK 0x88a -#define VOYAGER_MEMORY_CLICKMAP 0xa23 -#define VOYAGER_DUMP_LOCATION 0xb1a - -/* SUS In Control bit - used to tell SUS that we don't need to be - * babysat anymore */ -#define VOYAGER_SUS_IN_CONTROL_PORT 0x3ff -# define VOYAGER_IN_CONTROL_FLAG 0x80 - -/* Voyager PSI defines */ -#define VOYAGER_PSI_STATUS_REG 0x08 -# define PSI_DC_FAIL 0x01 -# define PSI_MON 0x02 -# define PSI_FAULT 0x04 -# define PSI_ALARM 0x08 -# define PSI_CURRENT 0x10 -# define PSI_DVM 0x20 -# define PSI_PSCFAULT 0x40 -# define PSI_STAT_CHG 0x80 - -#define VOYAGER_PSI_SUPPLY_REG 0x8000 - /* read */ -# define PSI_FAIL_DC 0x01 -# define PSI_FAIL_AC 0x02 -# define PSI_MON_INT 0x04 -# define PSI_SWITCH_OFF 0x08 -# define PSI_HX_OFF 0x10 -# define PSI_SECURITY 0x20 -# define PSI_CMOS_BATT_LOW 0x40 -# define PSI_CMOS_BATT_FAIL 0x80 - /* write */ -# define PSI_CLR_SWITCH_OFF 0x13 -# define PSI_CLR_HX_OFF 0x14 -# define PSI_CLR_CMOS_BATT_FAIL 0x17 - -#define VOYAGER_PSI_MASK 0x8001 -# define PSI_MASK_MASK 0x10 - -#define VOYAGER_PSI_AC_FAIL_REG 0x8004 -#define AC_FAIL_STAT_CHANGE 0x80 - -#define VOYAGER_PSI_GENERAL_REG 0x8007 - /* read */ -# define PSI_SWITCH_ON 0x01 -# define PSI_SWITCH_ENABLED 0x02 -# define PSI_ALARM_ENABLED 0x08 -# define PSI_SECURE_ENABLED 0x10 -# define PSI_COLD_RESET 0x20 -# define PSI_COLD_START 0x80 - /* write */ -# define PSI_POWER_DOWN 0x10 -# define PSI_SWITCH_DISABLE 0x01 -# define PSI_SWITCH_ENABLE 0x11 -# define PSI_CLEAR 0x12 -# define PSI_ALARM_DISABLE 0x03 -# define PSI_ALARM_ENABLE 0x13 -# define PSI_CLEAR_COLD_RESET 0x05 -# define PSI_SET_COLD_RESET 0x15 -# define PSI_CLEAR_COLD_START 0x07 -# define PSI_SET_COLD_START 0x17 - - - -struct voyager_bios_info { - __u8 len; - __u8 major; - __u8 minor; - __u8 debug; - __u8 num_classes; - __u8 class_1; - __u8 class_2; -}; - -/* The following structures and definitions are for the Kernel/SUS - * interface these are needed to find out how SUS initialised any Quad - * boards in the system */ - -#define NUMBER_OF_MC_BUSSES 2 -#define SLOTS_PER_MC_BUS 8 -#define MAX_CPUS 16 /* 16 way CPU system */ -#define MAX_PROCESSOR_BOARDS 4 /* 4 processor slot system */ -#define MAX_CACHE_LEVELS 4 /* # of cache levels supported */ -#define MAX_SHARED_CPUS 4 /* # of CPUs that can share a LARC */ -#define NUMBER_OF_POS_REGS 8 - -typedef struct { - __u8 MC_Slot; - __u8 POS_Values[NUMBER_OF_POS_REGS]; -} __attribute__((packed)) MC_SlotInformation_t; - -struct QuadDescription { - __u8 Type; /* for type 0 (DYADIC or MONADIC) all fields - * will be zero except for slot */ - __u8 StructureVersion; - __u32 CPI_BaseAddress; - __u32 LARC_BankSize; - __u32 LocalMemoryStateBits; - __u8 Slot; /* Processor slots 1 - 4 */ -} __attribute__((packed)); - -struct ProcBoardInfo { - __u8 Type; - __u8 StructureVersion; - __u8 NumberOfBoards; - struct QuadDescription QuadData[MAX_PROCESSOR_BOARDS]; -} __attribute__((packed)); - -struct CacheDescription { - __u8 Level; - __u32 TotalSize; - __u16 LineSize; - __u8 Associativity; - __u8 CacheType; - __u8 WriteType; - __u8 Number_CPUs_SharedBy; - __u8 Shared_CPUs_Hardware_IDs[MAX_SHARED_CPUS]; - -} __attribute__((packed)); - -struct CPU_Description { - __u8 CPU_HardwareId; - char *FRU_String; - __u8 NumberOfCacheLevels; - struct CacheDescription CacheLevelData[MAX_CACHE_LEVELS]; -} __attribute__((packed)); - -struct CPU_Info { - __u8 Type; - __u8 StructureVersion; - __u8 NumberOf_CPUs; - struct CPU_Description CPU_Data[MAX_CPUS]; -} __attribute__((packed)); - - -/* - * This structure will be used by SUS and the OS. - * The assumption about this structure is that no blank space is - * packed in it by our friend the compiler. - */ -typedef struct { - __u8 Mailbox_SUS; /* Written to by SUS to give - commands/response to the OS */ - __u8 Mailbox_OS; /* Written to by the OS to give - commands/response to SUS */ - __u8 SUS_MailboxVersion; /* Tells the OS which iteration of the - interface SUS supports */ - __u8 OS_MailboxVersion; /* Tells SUS which iteration of the - interface the OS supports */ - __u32 OS_Flags; /* Flags set by the OS as info for - SUS */ - __u32 SUS_Flags; /* Flags set by SUS as info - for the OS */ - __u32 WatchDogPeriod; /* Watchdog period (in seconds) which - the DP uses to see if the OS - is dead */ - __u32 WatchDogCount; /* Updated by the OS on every tic. */ - __u32 MemoryFor_SUS_ErrorLog; /* Flat 32 bit address which tells SUS - where to stuff the SUS error log - on a dump */ - MC_SlotInformation_t MC_SlotInfo[NUMBER_OF_MC_BUSSES*SLOTS_PER_MC_BUS]; - /* Storage for MCA POS data */ - /* All new SECOND_PASS_INTERFACE fields added from this point */ - struct ProcBoardInfo *BoardData; - struct CPU_Info *CPU_Data; - /* All new fields must be added from this point */ -} Voyager_KernelSUS_Mbox_t; - -/* structure for finding the right memory address to send a QIC CPI to */ -struct voyager_qic_cpi { - /* Each cache line (32 bytes) can trigger a cpi. The cpi - * read/write may occur anywhere in the cache line---pick the - * middle to be safe */ - struct { - __u32 pad1[3]; - __u32 cpi; - __u32 pad2[4]; - } qic_cpi[8]; -}; - -struct voyager_status { - __u32 power_fail:1; - __u32 switch_off:1; - __u32 request_from_kernel:1; -}; - -struct voyager_psi_regs { - __u8 cat_id; - __u8 cat_dev; - __u8 cat_control; - __u8 subaddr; - __u8 dummy4; - __u8 checkbit; - __u8 subaddr_low; - __u8 subaddr_high; - __u8 intstatus; - __u8 stat1; - __u8 stat3; - __u8 fault; - __u8 tms; - __u8 gen; - __u8 sysconf; - __u8 dummy15; -}; - -struct voyager_psi_subregs { - __u8 supply; - __u8 mask; - __u8 present; - __u8 DCfail; - __u8 ACfail; - __u8 fail; - __u8 UPSfail; - __u8 genstatus; -}; - -struct voyager_psi { - struct voyager_psi_regs regs; - struct voyager_psi_subregs subregs; -}; - -struct voyager_SUS { -#define VOYAGER_DUMP_BUTTON_NMI 0x1 -#define VOYAGER_SUS_VALID 0x2 -#define VOYAGER_SYSINT_COMPLETE 0x3 - __u8 SUS_mbox; -#define VOYAGER_NO_COMMAND 0x0 -#define VOYAGER_IGNORE_DUMP 0x1 -#define VOYAGER_DO_DUMP 0x2 -#define VOYAGER_SYSINT_HANDSHAKE 0x3 -#define VOYAGER_DO_MEM_DUMP 0x4 -#define VOYAGER_SYSINT_WAS_RECOVERED 0x5 - __u8 kernel_mbox; -#define VOYAGER_MAILBOX_VERSION 0x10 - __u8 SUS_version; - __u8 kernel_version; -#define VOYAGER_OS_HAS_SYSINT 0x1 -#define VOYAGER_OS_IN_PROGRESS 0x2 -#define VOYAGER_UPDATING_WDPERIOD 0x4 - __u32 kernel_flags; -#define VOYAGER_SUS_BOOTING 0x1 -#define VOYAGER_SUS_IN_PROGRESS 0x2 - __u32 SUS_flags; - __u32 watchdog_period; - __u32 watchdog_count; - __u32 SUS_errorlog; - /* lots of system configuration stuff under here */ -}; - -/* Variables exported by voyager_smp */ -extern __u32 voyager_extended_vic_processors; -extern __u32 voyager_allowed_boot_processors; -extern __u32 voyager_quad_processors; -extern struct voyager_qic_cpi *voyager_quad_cpi_addr[NR_CPUS]; -extern struct voyager_SUS *voyager_SUS; - -/* variables exported always */ -extern struct task_struct *voyager_thread; -extern int voyager_level; -extern struct voyager_status voyager_status; - -/* functions exported by the voyager and voyager_smp modules */ -extern int voyager_cat_readb(__u8 module, __u8 asic, int reg); -extern void voyager_cat_init(void); -extern void voyager_detect(struct voyager_bios_info *); -extern void voyager_trap_init(void); -extern void voyager_setup_irqs(void); -extern int voyager_memory_detect(int region, __u32 *addr, __u32 *length); -extern void voyager_smp_intr_init(void); -extern __u8 voyager_extended_cmos_read(__u16 cmos_address); -extern void voyager_smp_dump(void); -extern void voyager_timer_interrupt(void); -extern void smp_local_timer_interrupt(void); -extern void voyager_power_off(void); -extern void smp_voyager_power_off(void *dummy); -extern void voyager_restart(void); -extern void voyager_cat_power_off(void); -extern void voyager_cat_do_common_interrupt(void); -extern void voyager_handle_nmi(void); -extern void voyager_smp_intr_init(void); -/* Commands for the following are */ -#define VOYAGER_PSI_READ 0 -#define VOYAGER_PSI_WRITE 1 -#define VOYAGER_PSI_SUBREAD 2 -#define VOYAGER_PSI_SUBWRITE 3 -extern void voyager_cat_psi(__u8, __u16, __u8 *); - -/* These define the CPIs we use in linux */ -#define VIC_CPI_LEVEL0 0 -#define VIC_CPI_LEVEL1 1 -/* now the fake CPIs */ -#define VIC_TIMER_CPI 2 -#define VIC_INVALIDATE_CPI 3 -#define VIC_RESCHEDULE_CPI 4 -#define VIC_ENABLE_IRQ_CPI 5 -#define VIC_CALL_FUNCTION_CPI 6 -#define VIC_CALL_FUNCTION_SINGLE_CPI 7 - -/* Now the QIC CPIs: Since we don't need the two initial levels, - * these are 2 less than the VIC CPIs */ -#define QIC_CPI_OFFSET 1 -#define QIC_TIMER_CPI (VIC_TIMER_CPI - QIC_CPI_OFFSET) -#define QIC_INVALIDATE_CPI (VIC_INVALIDATE_CPI - QIC_CPI_OFFSET) -#define QIC_RESCHEDULE_CPI (VIC_RESCHEDULE_CPI - QIC_CPI_OFFSET) -#define QIC_ENABLE_IRQ_CPI (VIC_ENABLE_IRQ_CPI - QIC_CPI_OFFSET) -#define QIC_CALL_FUNCTION_CPI (VIC_CALL_FUNCTION_CPI - QIC_CPI_OFFSET) -#define QIC_CALL_FUNCTION_SINGLE_CPI (VIC_CALL_FUNCTION_SINGLE_CPI - QIC_CPI_OFFSET) - -#define VIC_START_FAKE_CPI VIC_TIMER_CPI -#define VIC_END_FAKE_CPI VIC_CALL_FUNCTION_SINGLE_CPI - -/* this is the SYS_INT CPI. */ -#define VIC_SYS_INT 8 -#define VIC_CMN_INT 15 - -/* This is the boot CPI for alternate processors. It gets overwritten - * by the above once the system has activated all available processors */ -#define VIC_CPU_BOOT_CPI VIC_CPI_LEVEL0 -#define VIC_CPU_BOOT_ERRATA_CPI (VIC_CPI_LEVEL0 + 8) - -extern asmlinkage void vic_cpi_interrupt(void); -extern asmlinkage void vic_sys_interrupt(void); -extern asmlinkage void vic_cmn_interrupt(void); -extern asmlinkage void qic_timer_interrupt(void); -extern asmlinkage void qic_invalidate_interrupt(void); -extern asmlinkage void qic_reschedule_interrupt(void); -extern asmlinkage void qic_enable_irq_interrupt(void); -extern asmlinkage void qic_call_function_interrupt(void); diff --git a/arch/x86/lguest/Kconfig b/arch/x86/lguest/Kconfig index c70e12b1a637..8dab8f7844d3 100644 --- a/arch/x86/lguest/Kconfig +++ b/arch/x86/lguest/Kconfig @@ -3,7 +3,6 @@ config LGUEST_GUEST select PARAVIRT depends on X86_32 depends on !X86_PAE - depends on !X86_VOYAGER select VIRTIO select VIRTIO_RING select VIRTIO_CONSOLE diff --git a/arch/x86/mach-voyager/Makefile b/arch/x86/mach-voyager/Makefile deleted file mode 100644 index 15c250b371d3..000000000000 --- a/arch/x86/mach-voyager/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the linux kernel. -# - -EXTRA_CFLAGS := -Iarch/x86/kernel -obj-y := setup.o voyager_basic.o voyager_thread.o - -obj-$(CONFIG_SMP) += voyager_smp.o voyager_cat.o diff --git a/arch/x86/mach-voyager/setup.c b/arch/x86/mach-voyager/setup.c deleted file mode 100644 index 88c3c555634f..000000000000 --- a/arch/x86/mach-voyager/setup.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Machine specific setup for generic - */ - -#include <linux/init.h> -#include <linux/interrupt.h> -#include <asm/arch_hooks.h> -#include <asm/voyager.h> -#include <asm/e820.h> -#include <asm/io.h> -#include <asm/setup.h> -#include <asm/cpu.h> - -void __init pre_intr_init_hook(void) -{ - init_ISA_irqs(); -} - -/* - * IRQ2 is cascade interrupt to second interrupt controller - */ -static struct irqaction irq2 = { - .handler = no_action, - .mask = CPU_MASK_NONE, - .name = "cascade", -}; - -void __init intr_init_hook(void) -{ -#ifdef CONFIG_SMP - voyager_smp_intr_init(); -#endif - - setup_irq(2, &irq2); -} - -static void voyager_disable_tsc(void) -{ - /* Voyagers run their CPUs from independent clocks, so disable - * the TSC code because we can't sync them */ - setup_clear_cpu_cap(X86_FEATURE_TSC); -} - -void __init pre_setup_arch_hook(void) -{ - voyager_disable_tsc(); -} - -void __init pre_time_init_hook(void) -{ - voyager_disable_tsc(); -} - -void __init trap_init_hook(void) -{ -} - -static struct irqaction irq0 = { - .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL | IRQF_TIMER, - .mask = CPU_MASK_NONE, - .name = "timer" -}; - -void __init time_init_hook(void) -{ - irq0.mask = cpumask_of_cpu(safe_smp_processor_id()); - setup_irq(0, &irq0); -} - -/* Hook for machine specific memory setup. */ - -char *__init machine_specific_memory_setup(void) -{ - char *who; - int new_nr; - - who = "NOT VOYAGER"; - - if (voyager_level == 5) { - __u32 addr, length; - int i; - - who = "Voyager-SUS"; - - e820.nr_map = 0; - for (i = 0; voyager_memory_detect(i, &addr, &length); i++) { - e820_add_region(addr, length, E820_RAM); - } - return who; - } else if (voyager_level == 4) { - __u32 tom; - __u16 catbase = inb(VOYAGER_SSPB_RELOCATION_PORT) << 8; - /* select the DINO config space */ - outb(VOYAGER_DINO, VOYAGER_CAT_CONFIG_PORT); - /* Read DINO top of memory register */ - tom = ((inb(catbase + 0x4) & 0xf0) << 16) - + ((inb(catbase + 0x5) & 0x7f) << 24); - - if (inb(catbase) != VOYAGER_DINO) { - printk(KERN_ERR - "Voyager: Failed to get DINO for L4, setting tom to EXT_MEM_K\n"); - tom = (boot_params.screen_info.ext_mem_k) << 10; - } - who = "Voyager-TOM"; - e820_add_region(0, 0x9f000, E820_RAM); - /* map from 1M to top of memory */ - e820_add_region(1 * 1024 * 1024, tom - 1 * 1024 * 1024, - E820_RAM); - /* FIXME: Should check the ASICs to see if I need to - * take out the 8M window. Just do it at the moment - * */ - e820_add_region(8 * 1024 * 1024, 8 * 1024 * 1024, - E820_RESERVED); - return who; - } - - return default_machine_specific_memory_setup(); -} diff --git a/arch/x86/mach-voyager/voyager_basic.c b/arch/x86/mach-voyager/voyager_basic.c deleted file mode 100644 index 46d6f8067690..000000000000 --- a/arch/x86/mach-voyager/voyager_basic.c +++ /dev/null @@ -1,317 +0,0 @@ -/* Copyright (C) 1999,2001 - * - * Author: J.E.J.Bottomley@HansenPartnership.com - * - * This file contains all the voyager specific routines for getting - * initialisation of the architecture to function. For additional - * features see: - * - * voyager_cat.c - Voyager CAT bus interface - * voyager_smp.c - Voyager SMP hal (emulates linux smp.c) - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/sched.h> -#include <linux/ptrace.h> -#include <linux/ioport.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/reboot.h> -#include <linux/sysrq.h> -#include <linux/smp.h> -#include <linux/nodemask.h> -#include <asm/io.h> -#include <asm/voyager.h> -#include <asm/vic.h> -#include <linux/pm.h> -#include <asm/tlbflush.h> -#include <asm/arch_hooks.h> -#include <asm/i8253.h> - -/* - * Power off function, if any - */ -void (*pm_power_off) (void); -EXPORT_SYMBOL(pm_power_off); - -int voyager_level = 0; - -struct voyager_SUS *voyager_SUS = NULL; - -#ifdef CONFIG_SMP -static void voyager_dump(int dummy1, struct tty_struct *dummy3) -{ - /* get here via a sysrq */ - voyager_smp_dump(); -} - -static struct sysrq_key_op sysrq_voyager_dump_op = { - .handler = voyager_dump, - .help_msg = "Voyager", - .action_msg = "Dump Voyager Status", -}; -#endif - -void voyager_detect(struct voyager_bios_info *bios) -{ - if (bios->len != 0xff) { - int class = (bios->class_1 << 8) - | (bios->class_2 & 0xff); - - printk("Voyager System detected.\n" - " Class %x, Revision %d.%d\n", - class, bios->major, bios->minor); - if (class == VOYAGER_LEVEL4) - voyager_level = 4; - else if (class < VOYAGER_LEVEL5_AND_ABOVE) - voyager_level = 3; - else - voyager_level = 5; - printk(" Architecture Level %d\n", voyager_level); - if (voyager_level < 4) - printk - ("\n**WARNING**: Voyager HAL only supports Levels 4 and 5 Architectures at the moment\n\n"); - /* install the power off handler */ - pm_power_off = voyager_power_off; -#ifdef CONFIG_SMP - register_sysrq_key('v', &sysrq_voyager_dump_op); -#endif - } else { - printk("\n\n**WARNING**: No Voyager Subsystem Found\n"); - } -} - -void voyager_system_interrupt(int cpl, void *dev_id) -{ - printk("Voyager: detected system interrupt\n"); -} - -/* Routine to read information from the extended CMOS area */ -__u8 voyager_extended_cmos_read(__u16 addr) -{ - outb(addr & 0xff, 0x74); - outb((addr >> 8) & 0xff, 0x75); - return inb(0x76); -} - -/* internal definitions for the SUS Click Map of memory */ - -#define CLICK_ENTRIES 16 -#define CLICK_SIZE 4096 /* click to byte conversion for Length */ - -typedef struct ClickMap { - struct Entry { - __u32 Address; - __u32 Length; - } Entry[CLICK_ENTRIES]; -} ClickMap_t; - -/* This routine is pretty much an awful hack to read the bios clickmap by - * mapping it into page 0. There are usually three regions in the map: - * Base Memory - * Extended Memory - * zero length marker for end of map - * - * Returns are 0 for failure and 1 for success on extracting region. - */ -int __init voyager_memory_detect(int region, __u32 * start, __u32 * length) -{ - int i; - int retval = 0; - __u8 cmos[4]; - ClickMap_t *map; - unsigned long map_addr; - unsigned long old; - - if (region >= CLICK_ENTRIES) { - printk("Voyager: Illegal ClickMap region %d\n", region); - return 0; - } - - for (i = 0; i < sizeof(cmos); i++) - cmos[i] = - voyager_extended_cmos_read(VOYAGER_MEMORY_CLICKMAP + i); - - map_addr = *(unsigned long *)cmos; - - /* steal page 0 for this */ - old = pg0[0]; - pg0[0] = ((map_addr & PAGE_MASK) | _PAGE_RW | _PAGE_PRESENT); - local_flush_tlb(); - /* now clear everything out but page 0 */ - map = (ClickMap_t *) (map_addr & (~PAGE_MASK)); - - /* zero length is the end of the clickmap */ - if (map->Entry[region].Length != 0) { - *length = map->Entry[region].Length * CLICK_SIZE; - *start = map->Entry[region].Address; - retval = 1; - } - - /* replace the mapping */ - pg0[0] = old; - local_flush_tlb(); - return retval; -} - -/* voyager specific handling code for timer interrupts. Used to hand - * off the timer tick to the SMP code, since the VIC doesn't have an - * internal timer (The QIC does, but that's another story). */ -void voyager_timer_interrupt(void) -{ - if ((jiffies & 0x3ff) == 0) { - - /* There seems to be something flaky in either - * hardware or software that is resetting the timer 0 - * count to something much higher than it should be - * This seems to occur in the boot sequence, just - * before root is mounted. Therefore, every 10 - * seconds or so, we sanity check the timer zero count - * and kick it back to where it should be. - * - * FIXME: This is the most awful hack yet seen. I - * should work out exactly what is interfering with - * the timer count settings early in the boot sequence - * and swiftly introduce it to something sharp and - * pointy. */ - __u16 val; - - spin_lock(&i8253_lock); - - outb_p(0x00, 0x43); - val = inb_p(0x40); - val |= inb(0x40) << 8; - spin_unlock(&i8253_lock); - - if (val > LATCH) { - printk - ("\nVOYAGER: countdown timer value too high (%d), resetting\n\n", - val); - spin_lock(&i8253_lock); - outb(0x34, 0x43); - outb_p(LATCH & 0xff, 0x40); /* LSB */ - outb(LATCH >> 8, 0x40); /* MSB */ - spin_unlock(&i8253_lock); - } - } -#ifdef CONFIG_SMP - smp_vic_timer_interrupt(); -#endif -} - -void voyager_power_off(void) -{ - printk("VOYAGER Power Off\n"); - - if (voyager_level == 5) { - voyager_cat_power_off(); - } else if (voyager_level == 4) { - /* This doesn't apparently work on most L4 machines, - * but the specs say to do this to get automatic power - * off. Unfortunately, if it doesn't power off the - * machine, it ends up doing a cold restart, which - * isn't really intended, so comment out the code */ -#if 0 - int port; - - /* enable the voyager Configuration Space */ - outb((inb(VOYAGER_MC_SETUP) & 0xf0) | 0x8, VOYAGER_MC_SETUP); - /* the port for the power off flag is an offset from the - floating base */ - port = (inb(VOYAGER_SSPB_RELOCATION_PORT) << 8) + 0x21; - /* set the power off flag */ - outb(inb(port) | 0x1, port); -#endif - } - /* and wait for it to happen */ - local_irq_disable(); - for (;;) - halt(); -} - -/* copied from process.c */ -static inline void kb_wait(void) -{ - int i; - - for (i = 0; i < 0x10000; i++) - if ((inb_p(0x64) & 0x02) == 0) - break; -} - -void machine_shutdown(void) -{ - /* Architecture specific shutdown needed before a kexec */ -} - -void machine_restart(char *cmd) -{ - printk("Voyager Warm Restart\n"); - kb_wait(); - - if (voyager_level == 5) { - /* write magic values to the RTC to inform system that - * shutdown is beginning */ - outb(0x8f, 0x70); - outb(0x5, 0x71); - - udelay(50); - outb(0xfe, 0x64); /* pull reset low */ - } else if (voyager_level == 4) { - __u16 catbase = inb(VOYAGER_SSPB_RELOCATION_PORT) << 8; - __u8 basebd = inb(VOYAGER_MC_SETUP); - - outb(basebd | 0x08, VOYAGER_MC_SETUP); - outb(0x02, catbase + 0x21); - } - local_irq_disable(); - for (;;) - halt(); -} - -void machine_emergency_restart(void) -{ - /*for now, just hook this to a warm restart */ - machine_restart(NULL); -} - -void mca_nmi_hook(void) -{ - __u8 dumpval __maybe_unused = inb(0xf823); - __u8 swnmi __maybe_unused = inb(0xf813); - - /* FIXME: assume dump switch pressed */ - /* check to see if the dump switch was pressed */ - VDEBUG(("VOYAGER: dumpval = 0x%x, swnmi = 0x%x\n", dumpval, swnmi)); - /* clear swnmi */ - outb(0xff, 0xf813); - /* tell SUS to ignore dump */ - if (voyager_level == 5 && voyager_SUS != NULL) { - if (voyager_SUS->SUS_mbox == VOYAGER_DUMP_BUTTON_NMI) { - voyager_SUS->kernel_mbox = VOYAGER_NO_COMMAND; - voyager_SUS->kernel_flags |= VOYAGER_OS_IN_PROGRESS; - udelay(1000); - voyager_SUS->kernel_mbox = VOYAGER_IGNORE_DUMP; - voyager_SUS->kernel_flags &= ~VOYAGER_OS_IN_PROGRESS; - } - } - printk(KERN_ERR - "VOYAGER: Dump switch pressed, printing CPU%d tracebacks\n", - smp_processor_id()); - show_stack(NULL, NULL); - show_state(); -} - -void machine_halt(void) -{ - /* treat a halt like a power off */ - machine_power_off(); -} - -void machine_power_off(void) -{ - if (pm_power_off) - pm_power_off(); -} diff --git a/arch/x86/mach-voyager/voyager_cat.c b/arch/x86/mach-voyager/voyager_cat.c deleted file mode 100644 index 2ad598c104af..000000000000 --- a/arch/x86/mach-voyager/voyager_cat.c +++ /dev/null @@ -1,1197 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8 -*- */ - -/* Copyright (C) 1999,2001 - * - * Author: J.E.J.Bottomley@HansenPartnership.com - * - * This file contains all the logic for manipulating the CAT bus - * in a level 5 machine. - * - * The CAT bus is a serial configuration and test bus. Its primary - * uses are to probe the initial configuration of the system and to - * diagnose error conditions when a system interrupt occurs. The low - * level interface is fairly primitive, so most of this file consists - * of bit shift manipulations to send and receive packets on the - * serial bus */ - -#include <linux/types.h> -#include <linux/completion.h> -#include <linux/sched.h> -#include <asm/voyager.h> -#include <asm/vic.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <asm/io.h> - -#ifdef VOYAGER_CAT_DEBUG -#define CDEBUG(x) printk x -#else -#define CDEBUG(x) -#endif - -/* the CAT command port */ -#define CAT_CMD (sspb + 0xe) -/* the CAT data port */ -#define CAT_DATA (sspb + 0xd) - -/* the internal cat functions */ -static void cat_pack(__u8 * msg, __u16 start_bit, __u8 * data, __u16 num_bits); -static void cat_unpack(__u8 * msg, __u16 start_bit, __u8 * data, - __u16 num_bits); -static void cat_build_header(__u8 * header, const __u16 len, - const __u16 smallest_reg_bits, - const __u16 longest_reg_bits); -static int cat_sendinst(voyager_module_t * modp, voyager_asic_t * asicp, - __u8 reg, __u8 op); -static int cat_getdata(voyager_module_t * modp, voyager_asic_t * asicp, - __u8 reg, __u8 * value); -static int cat_shiftout(__u8 * data, __u16 data_bytes, __u16 header_bytes, - __u8 pad_bits); -static int cat_write(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, - __u8 value); -static int cat_read(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, - __u8 * value); -static int cat_subread(voyager_module_t * modp, voyager_asic_t * asicp, - __u16 offset, __u16 len, void *buf); -static int cat_senddata(voyager_module_t * modp, voyager_asic_t * asicp, - __u8 reg, __u8 value); -static int cat_disconnect(voyager_module_t * modp, voyager_asic_t * asicp); -static int cat_connect(voyager_module_t * modp, voyager_asic_t * asicp); - -static inline const char *cat_module_name(int module_id) -{ - switch (module_id) { - case 0x10: - return "Processor Slot 0"; - case 0x11: - return "Processor Slot 1"; - case 0x12: - return "Processor Slot 2"; - case 0x13: - return "Processor Slot 4"; - case 0x14: - return "Memory Slot 0"; - case 0x15: - return "Memory Slot 1"; - case 0x18: - return "Primary Microchannel"; - case 0x19: - return "Secondary Microchannel"; - case 0x1a: - return "Power Supply Interface"; - case 0x1c: - return "Processor Slot 5"; - case 0x1d: - return "Processor Slot 6"; - case 0x1e: - return "Processor Slot 7"; - case 0x1f: - return "Processor Slot 8"; - default: - return "Unknown Module"; - } -} - -static int sspb = 0; /* stores the super port location */ -int voyager_8slot = 0; /* set to true if a 51xx monster */ - -voyager_module_t *voyager_cat_list; - -/* the I/O port assignments for the VIC and QIC */ -static struct resource vic_res = { - .name = "Voyager Interrupt Controller", - .start = 0xFC00, - .end = 0xFC6F -}; -static struct resource qic_res = { - .name = "Quad Interrupt Controller", - .start = 0xFC70, - .end = 0xFCFF -}; - -/* This function is used to pack a data bit stream inside a message. - * It writes num_bits of the data buffer in msg starting at start_bit. - * Note: This function assumes that any unused bit in the data stream - * is set to zero so that the ors will work correctly */ -static void -cat_pack(__u8 * msg, const __u16 start_bit, __u8 * data, const __u16 num_bits) -{ - /* compute initial shift needed */ - const __u16 offset = start_bit % BITS_PER_BYTE; - __u16 len = num_bits / BITS_PER_BYTE; - __u16 byte = start_bit / BITS_PER_BYTE; - __u16 residue = (num_bits % BITS_PER_BYTE) + offset; - int i; - - /* adjust if we have more than a byte of residue */ - if (residue >= BITS_PER_BYTE) { - residue -= BITS_PER_BYTE; - len++; - } - - /* clear out the bits. We assume here that if len==0 then - * residue >= offset. This is always true for the catbus - * operations */ - msg[byte] &= 0xff << (BITS_PER_BYTE - offset); - msg[byte++] |= data[0] >> offset; - if (len == 0) - return; - for (i = 1; i < len; i++) - msg[byte++] = (data[i - 1] << (BITS_PER_BYTE - offset)) - | (data[i] >> offset); - if (residue != 0) { - __u8 mask = 0xff >> residue; - __u8 last_byte = data[i - 1] << (BITS_PER_BYTE - offset) - | (data[i] >> offset); - - last_byte &= ~mask; - msg[byte] &= mask; - msg[byte] |= last_byte; - } - return; -} - -/* unpack the data again (same arguments as cat_pack()). data buffer - * must be zero populated. - * - * Function: given a message string move to start_bit and copy num_bits into - * data (starting at bit 0 in data). - */ -static void -cat_unpack(__u8 * msg, const __u16 start_bit, __u8 * data, const __u16 num_bits) -{ - /* compute initial shift needed */ - const __u16 offset = start_bit % BITS_PER_BYTE; - __u16 len = num_bits / BITS_PER_BYTE; - const __u8 last_bits = num_bits % BITS_PER_BYTE; - __u16 byte = start_bit / BITS_PER_BYTE; - int i; - - if (last_bits != 0) - len++; - - /* special case: want < 8 bits from msg and we can get it from - * a single byte of the msg */ - if (len == 0 && BITS_PER_BYTE - offset >= num_bits) { - data[0] = msg[byte] << offset; - data[0] &= 0xff >> (BITS_PER_BYTE - num_bits); - return; - } - for (i = 0; i < len; i++) { - /* this annoying if has to be done just in case a read of - * msg one beyond the array causes a panic */ - if (offset != 0) { - data[i] = msg[byte++] << offset; - data[i] |= msg[byte] >> (BITS_PER_BYTE - offset); - } else { - data[i] = msg[byte++]; - } - } - /* do we need to truncate the final byte */ - if (last_bits != 0) { - data[i - 1] &= 0xff << (BITS_PER_BYTE - last_bits); - } - return; -} - -static void -cat_build_header(__u8 * header, const __u16 len, const __u16 smallest_reg_bits, - const __u16 longest_reg_bits) -{ - int i; - __u16 start_bit = (smallest_reg_bits - 1) % BITS_PER_BYTE; - __u8 *last_byte = &header[len - 1]; - - if (start_bit == 0) - start_bit = 1; /* must have at least one bit in the hdr */ - - for (i = 0; i < len; i++) - header[i] = 0; - - for (i = start_bit; i > 0; i--) - *last_byte = ((*last_byte) << 1) + 1; - -} - -static int -cat_sendinst(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, __u8 op) -{ - __u8 parity, inst, inst_buf[4] = { 0 }; - __u8 iseq[VOYAGER_MAX_SCAN_PATH], hseq[VOYAGER_MAX_REG_SIZE]; - __u16 ibytes, hbytes, padbits; - int i; - - /* - * Parity is the parity of the register number + 1 (READ_REGISTER - * and WRITE_REGISTER always add '1' to the number of bits == 1) - */ - parity = (__u8) (1 + (reg & 0x01) + - ((__u8) (reg & 0x02) >> 1) + - ((__u8) (reg & 0x04) >> 2) + - ((__u8) (reg & 0x08) >> 3)) % 2; - - inst = ((parity << 7) | (reg << 2) | op); - - outb(VOYAGER_CAT_IRCYC, CAT_CMD); - if (!modp->scan_path_connected) { - if (asicp->asic_id != VOYAGER_CAT_ID) { - printk - ("**WARNING***: cat_sendinst has disconnected scan path not to CAT asic\n"); - return 1; - } - outb(VOYAGER_CAT_HEADER, CAT_DATA); - outb(inst, CAT_DATA); - if (inb(CAT_DATA) != VOYAGER_CAT_HEADER) { - CDEBUG(("VOYAGER CAT: cat_sendinst failed to get CAT_HEADER\n")); - return 1; - } - return 0; - } - ibytes = modp->inst_bits / BITS_PER_BYTE; - if ((padbits = modp->inst_bits % BITS_PER_BYTE) != 0) { - padbits = BITS_PER_BYTE - padbits; - ibytes++; - } - hbytes = modp->largest_reg / BITS_PER_BYTE; - if (modp->largest_reg % BITS_PER_BYTE) - hbytes++; - CDEBUG(("cat_sendinst: ibytes=%d, hbytes=%d\n", ibytes, hbytes)); - /* initialise the instruction sequence to 0xff */ - for (i = 0; i < ibytes + hbytes; i++) - iseq[i] = 0xff; - cat_build_header(hseq, hbytes, modp->smallest_reg, modp->largest_reg); - cat_pack(iseq, modp->inst_bits, hseq, hbytes * BITS_PER_BYTE); - inst_buf[0] = inst; - inst_buf[1] = 0xFF >> (modp->largest_reg % BITS_PER_BYTE); - cat_pack(iseq, asicp->bit_location, inst_buf, asicp->ireg_length); -#ifdef VOYAGER_CAT_DEBUG - printk("ins = 0x%x, iseq: ", inst); - for (i = 0; i < ibytes + hbytes; i++) - printk("0x%x ", iseq[i]); - printk("\n"); -#endif - if (cat_shiftout(iseq, ibytes, hbytes, padbits)) { - CDEBUG(("VOYAGER CAT: cat_sendinst: cat_shiftout failed\n")); - return 1; - } - CDEBUG(("CAT SHIFTOUT DONE\n")); - return 0; -} - -static int -cat_getdata(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, - __u8 * value) -{ - if (!modp->scan_path_connected) { - if (asicp->asic_id != VOYAGER_CAT_ID) { - CDEBUG(("VOYAGER CAT: ERROR: cat_getdata to CAT asic with scan path connected\n")); - return 1; - } - if (reg > VOYAGER_SUBADDRHI) - outb(VOYAGER_CAT_RUN, CAT_CMD); - outb(VOYAGER_CAT_DRCYC, CAT_CMD); - outb(VOYAGER_CAT_HEADER, CAT_DATA); - *value = inb(CAT_DATA); - outb(0xAA, CAT_DATA); - if (inb(CAT_DATA) != VOYAGER_CAT_HEADER) { - CDEBUG(("cat_getdata: failed to get VOYAGER_CAT_HEADER\n")); - return 1; - } - return 0; - } else { - __u16 sbits = modp->num_asics - 1 + asicp->ireg_length; - __u16 sbytes = sbits / BITS_PER_BYTE; - __u16 tbytes; - __u8 string[VOYAGER_MAX_SCAN_PATH], - trailer[VOYAGER_MAX_REG_SIZE]; - __u8 padbits; - int i; - - outb(VOYAGER_CAT_DRCYC, CAT_CMD); - - if ((padbits = sbits % BITS_PER_BYTE) != 0) { - padbits = BITS_PER_BYTE - padbits; - sbytes++; - } - tbytes = asicp->ireg_length / BITS_PER_BYTE; - if (asicp->ireg_length % BITS_PER_BYTE) - tbytes++; - CDEBUG(("cat_getdata: tbytes = %d, sbytes = %d, padbits = %d\n", - tbytes, sbytes, padbits)); - cat_build_header(trailer, tbytes, 1, asicp->ireg_length); - - for (i = tbytes - 1; i >= 0; i--) { - outb(trailer[i], CAT_DATA); - string[sbytes + i] = inb(CAT_DATA); - } - - for (i = sbytes - 1; i >= 0; i--) { - outb(0xaa, CAT_DATA); - string[i] = inb(CAT_DATA); - } - *value = 0; - cat_unpack(string, - padbits + (tbytes * BITS_PER_BYTE) + - asicp->asic_location, value, asicp->ireg_length); -#ifdef VOYAGER_CAT_DEBUG - printk("value=0x%x, string: ", *value); - for (i = 0; i < tbytes + sbytes; i++) - printk("0x%x ", string[i]); - printk("\n"); -#endif - - /* sanity check the rest of the return */ - for (i = 0; i < tbytes; i++) { - __u8 input = 0; - - cat_unpack(string, padbits + (i * BITS_PER_BYTE), - &input, BITS_PER_BYTE); - if (trailer[i] != input) { - CDEBUG(("cat_getdata: failed to sanity check rest of ret(%d) 0x%x != 0x%x\n", i, input, trailer[i])); - return 1; - } - } - CDEBUG(("cat_getdata DONE\n")); - return 0; - } -} - -static int -cat_shiftout(__u8 * data, __u16 data_bytes, __u16 header_bytes, __u8 pad_bits) -{ - int i; - - for (i = data_bytes + header_bytes - 1; i >= header_bytes; i--) - outb(data[i], CAT_DATA); - - for (i = header_bytes - 1; i >= 0; i--) { - __u8 header = 0; - __u8 input; - - outb(data[i], CAT_DATA); - input = inb(CAT_DATA); - CDEBUG(("cat_shiftout: returned 0x%x\n", input)); - cat_unpack(data, ((data_bytes + i) * BITS_PER_BYTE) - pad_bits, - &header, BITS_PER_BYTE); - if (input != header) { - CDEBUG(("VOYAGER CAT: cat_shiftout failed to return header 0x%x != 0x%x\n", input, header)); - return 1; - } - } - return 0; -} - -static int -cat_senddata(voyager_module_t * modp, voyager_asic_t * asicp, - __u8 reg, __u8 value) -{ - outb(VOYAGER_CAT_DRCYC, CAT_CMD); - if (!modp->scan_path_connected) { - if (asicp->asic_id != VOYAGER_CAT_ID) { - CDEBUG(("VOYAGER CAT: ERROR: scan path disconnected when asic != CAT\n")); - return 1; - } - outb(VOYAGER_CAT_HEADER, CAT_DATA); - outb(value, CAT_DATA); - if (inb(CAT_DATA) != VOYAGER_CAT_HEADER) { - CDEBUG(("cat_senddata: failed to get correct header response to sent data\n")); - return 1; - } - if (reg > VOYAGER_SUBADDRHI) { - outb(VOYAGER_CAT_RUN, CAT_CMD); - outb(VOYAGER_CAT_END, CAT_CMD); - outb(VOYAGER_CAT_RUN, CAT_CMD); - } - - return 0; - } else { - __u16 hbytes = asicp->ireg_length / BITS_PER_BYTE; - __u16 dbytes = - (modp->num_asics - 1 + asicp->ireg_length) / BITS_PER_BYTE; - __u8 padbits, dseq[VOYAGER_MAX_SCAN_PATH], - hseq[VOYAGER_MAX_REG_SIZE]; - int i; - - if ((padbits = (modp->num_asics - 1 - + asicp->ireg_length) % BITS_PER_BYTE) != 0) { - padbits = BITS_PER_BYTE - padbits; - dbytes++; - } - if (asicp->ireg_length % BITS_PER_BYTE) - hbytes++; - - cat_build_header(hseq, hbytes, 1, asicp->ireg_length); - - for (i = 0; i < dbytes + hbytes; i++) - dseq[i] = 0xff; - CDEBUG(("cat_senddata: dbytes=%d, hbytes=%d, padbits=%d\n", - dbytes, hbytes, padbits)); - cat_pack(dseq, modp->num_asics - 1 + asicp->ireg_length, - hseq, hbytes * BITS_PER_BYTE); - cat_pack(dseq, asicp->asic_location, &value, - asicp->ireg_length); -#ifdef VOYAGER_CAT_DEBUG - printk("dseq "); - for (i = 0; i < hbytes + dbytes; i++) { - printk("0x%x ", dseq[i]); - } - printk("\n"); -#endif - return cat_shiftout(dseq, dbytes, hbytes, padbits); - } -} - -static int -cat_write(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, __u8 value) -{ - if (cat_sendinst(modp, asicp, reg, VOYAGER_WRITE_CONFIG)) - return 1; - return cat_senddata(modp, asicp, reg, value); -} - -static int -cat_read(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, - __u8 * value) -{ - if (cat_sendinst(modp, asicp, reg, VOYAGER_READ_CONFIG)) - return 1; - return cat_getdata(modp, asicp, reg, value); -} - -static int -cat_subaddrsetup(voyager_module_t * modp, voyager_asic_t * asicp, __u16 offset, - __u16 len) -{ - __u8 val; - - if (len > 1) { - /* set auto increment */ - __u8 newval; - - if (cat_read(modp, asicp, VOYAGER_AUTO_INC_REG, &val)) { - CDEBUG(("cat_subaddrsetup: read of VOYAGER_AUTO_INC_REG failed\n")); - return 1; - } - CDEBUG(("cat_subaddrsetup: VOYAGER_AUTO_INC_REG = 0x%x\n", - val)); - newval = val | VOYAGER_AUTO_INC; - if (newval != val) { - if (cat_write(modp, asicp, VOYAGER_AUTO_INC_REG, val)) { - CDEBUG(("cat_subaddrsetup: write to VOYAGER_AUTO_INC_REG failed\n")); - return 1; - } - } - } - if (cat_write(modp, asicp, VOYAGER_SUBADDRLO, (__u8) (offset & 0xff))) { - CDEBUG(("cat_subaddrsetup: write to SUBADDRLO failed\n")); - return 1; - } - if (asicp->subaddr > VOYAGER_SUBADDR_LO) { - if (cat_write - (modp, asicp, VOYAGER_SUBADDRHI, (__u8) (offset >> 8))) { - CDEBUG(("cat_subaddrsetup: write to SUBADDRHI failed\n")); - return 1; - } - cat_read(modp, asicp, VOYAGER_SUBADDRHI, &val); - CDEBUG(("cat_subaddrsetup: offset = %d, hi = %d\n", offset, - val)); - } - cat_read(modp, asicp, VOYAGER_SUBADDRLO, &val); - CDEBUG(("cat_subaddrsetup: offset = %d, lo = %d\n", offset, val)); - return 0; -} - -static int -cat_subwrite(voyager_module_t * modp, voyager_asic_t * asicp, __u16 offset, - __u16 len, void *buf) -{ - int i, retval; - - /* FIXME: need special actions for VOYAGER_CAT_ID here */ - if (asicp->asic_id == VOYAGER_CAT_ID) { - CDEBUG(("cat_subwrite: ATTEMPT TO WRITE TO CAT ASIC\n")); - /* FIXME -- This is supposed to be handled better - * There is a problem writing to the cat asic in the - * PSI. The 30us delay seems to work, though */ - udelay(30); - } - - if ((retval = cat_subaddrsetup(modp, asicp, offset, len)) != 0) { - printk("cat_subwrite: cat_subaddrsetup FAILED\n"); - return retval; - } - - if (cat_sendinst - (modp, asicp, VOYAGER_SUBADDRDATA, VOYAGER_WRITE_CONFIG)) { - printk("cat_subwrite: cat_sendinst FAILED\n"); - return 1; - } - for (i = 0; i < len; i++) { - if (cat_senddata(modp, asicp, 0xFF, ((__u8 *) buf)[i])) { - printk - ("cat_subwrite: cat_sendata element at %d FAILED\n", - i); - return 1; - } - } - return 0; -} -static int -cat_subread(voyager_module_t * modp, voyager_asic_t * asicp, __u16 offset, - __u16 len, void *buf) -{ - int i, retval; - - if ((retval = cat_subaddrsetup(modp, asicp, offset, len)) != 0) { - CDEBUG(("cat_subread: cat_subaddrsetup FAILED\n")); - return retval; - } - - if (cat_sendinst(modp, asicp, VOYAGER_SUBADDRDATA, VOYAGER_READ_CONFIG)) { - CDEBUG(("cat_subread: cat_sendinst failed\n")); - return 1; - } - for (i = 0; i < len; i++) { - if (cat_getdata(modp, asicp, 0xFF, &((__u8 *) buf)[i])) { - CDEBUG(("cat_subread: cat_getdata element %d failed\n", - i)); - return 1; - } - } - return 0; -} - -/* buffer for storing EPROM data read in during initialisation */ -static __initdata __u8 eprom_buf[0xFFFF]; -static voyager_module_t *voyager_initial_module; - -/* Initialise the cat bus components. We assume this is called by the - * boot cpu *after* all memory initialisation has been done (so we can - * use kmalloc) but before smp initialisation, so we can probe the SMP - * configuration and pick up necessary information. */ -void __init voyager_cat_init(void) -{ - voyager_module_t **modpp = &voyager_initial_module; - voyager_asic_t **asicpp; - voyager_asic_t *qabc_asic = NULL; - int i, j; - unsigned long qic_addr = 0; - __u8 qabc_data[0x20]; - __u8 num_submodules, val; - voyager_eprom_hdr_t *eprom_hdr = (voyager_eprom_hdr_t *) & eprom_buf[0]; - - __u8 cmos[4]; - unsigned long addr; - - /* initiallise the SUS mailbox */ - for (i = 0; i < sizeof(cmos); i++) - cmos[i] = voyager_extended_cmos_read(VOYAGER_DUMP_LOCATION + i); - addr = *(unsigned long *)cmos; - if ((addr & 0xff000000) != 0xff000000) { - printk(KERN_ERR - "Voyager failed to get SUS mailbox (addr = 0x%lx\n", - addr); - } else { - static struct resource res; - - res.name = "voyager SUS"; - res.start = addr; - res.end = addr + 0x3ff; - - request_resource(&iomem_resource, &res); - voyager_SUS = (struct voyager_SUS *) - ioremap(addr, 0x400); - printk(KERN_NOTICE "Voyager SUS mailbox version 0x%x\n", - voyager_SUS->SUS_version); - voyager_SUS->kernel_version = VOYAGER_MAILBOX_VERSION; - voyager_SUS->kernel_flags = VOYAGER_OS_HAS_SYSINT; - } - - /* clear the processor counts */ - voyager_extended_vic_processors = 0; - voyager_quad_processors = 0; - - printk("VOYAGER: beginning CAT bus probe\n"); - /* set up the SuperSet Port Block which tells us where the - * CAT communication port is */ - sspb = inb(VOYAGER_SSPB_RELOCATION_PORT) * 0x100; - VDEBUG(("VOYAGER DEBUG: sspb = 0x%x\n", sspb)); - - /* now find out if were 8 slot or normal */ - if ((inb(VIC_PROC_WHO_AM_I) & EIGHT_SLOT_IDENTIFIER) - == EIGHT_SLOT_IDENTIFIER) { - voyager_8slot = 1; - printk(KERN_NOTICE - "Voyager: Eight slot 51xx configuration detected\n"); - } - - for (i = VOYAGER_MIN_MODULE; i <= VOYAGER_MAX_MODULE; i++) { - __u8 input; - int asic; - __u16 eprom_size; - __u16 sp_offset; - - outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); - outb(i, VOYAGER_CAT_CONFIG_PORT); - - /* check the presence of the module */ - outb(VOYAGER_CAT_RUN, CAT_CMD); - outb(VOYAGER_CAT_IRCYC, CAT_CMD); - outb(VOYAGER_CAT_HEADER, CAT_DATA); - /* stream series of alternating 1's and 0's to stimulate - * response */ - outb(0xAA, CAT_DATA); - input = inb(CAT_DATA); - outb(VOYAGER_CAT_END, CAT_CMD); - if (input != VOYAGER_CAT_HEADER) { - continue; - } - CDEBUG(("VOYAGER DEBUG: found module id 0x%x, %s\n", i, - cat_module_name(i))); - *modpp = kmalloc(sizeof(voyager_module_t), GFP_KERNEL); /*&voyager_module_storage[cat_count++]; */ - if (*modpp == NULL) { - printk("**WARNING** kmalloc failure in cat_init\n"); - continue; - } - memset(*modpp, 0, sizeof(voyager_module_t)); - /* need temporary asic for cat_subread. It will be - * filled in correctly later */ - (*modpp)->asic = kmalloc(sizeof(voyager_asic_t), GFP_KERNEL); /*&voyager_asic_storage[asic_count]; */ - if ((*modpp)->asic == NULL) { - printk("**WARNING** kmalloc failure in cat_init\n"); - continue; - } - memset((*modpp)->asic, 0, sizeof(voyager_asic_t)); - (*modpp)->asic->asic_id = VOYAGER_CAT_ID; - (*modpp)->asic->subaddr = VOYAGER_SUBADDR_HI; - (*modpp)->module_addr = i; - (*modpp)->scan_path_connected = 0; - if (i == VOYAGER_PSI) { - /* Exception leg for modules with no EEPROM */ - printk("Module \"%s\"\n", cat_module_name(i)); - continue; - } - - CDEBUG(("cat_init: Reading eeprom for module 0x%x at offset %d\n", i, VOYAGER_XSUM_END_OFFSET)); - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_disconnect(*modpp, (*modpp)->asic); - if (cat_subread(*modpp, (*modpp)->asic, - VOYAGER_XSUM_END_OFFSET, sizeof(eprom_size), - &eprom_size)) { - printk - ("**WARNING**: Voyager couldn't read EPROM size for module 0x%x\n", - i); - outb(VOYAGER_CAT_END, CAT_CMD); - continue; - } - if (eprom_size > sizeof(eprom_buf)) { - printk - ("**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d\n", - i, eprom_size); - outb(VOYAGER_CAT_END, CAT_CMD); - continue; - } - outb(VOYAGER_CAT_END, CAT_CMD); - outb(VOYAGER_CAT_RUN, CAT_CMD); - CDEBUG(("cat_init: module 0x%x, eeprom_size %d\n", i, - eprom_size)); - if (cat_subread - (*modpp, (*modpp)->asic, 0, eprom_size, eprom_buf)) { - outb(VOYAGER_CAT_END, CAT_CMD); - continue; - } - outb(VOYAGER_CAT_END, CAT_CMD); - printk("Module \"%s\", version 0x%x, tracer 0x%x, asics %d\n", - cat_module_name(i), eprom_hdr->version_id, - *((__u32 *) eprom_hdr->tracer), eprom_hdr->num_asics); - (*modpp)->ee_size = eprom_hdr->ee_size; - (*modpp)->num_asics = eprom_hdr->num_asics; - asicpp = &((*modpp)->asic); - sp_offset = eprom_hdr->scan_path_offset; - /* All we really care about are the Quad cards. We - * identify them because they are in a processor slot - * and have only four asics */ - if ((i < 0x10 || (i >= 0x14 && i < 0x1c) || i > 0x1f)) { - modpp = &((*modpp)->next); - continue; - } - /* Now we know it's in a processor slot, does it have - * a quad baseboard submodule */ - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_read(*modpp, (*modpp)->asic, VOYAGER_SUBMODPRESENT, - &num_submodules); - /* lowest two bits, active low */ - num_submodules = ~(0xfc | num_submodules); - CDEBUG(("VOYAGER CAT: %d submodules present\n", - num_submodules)); - if (num_submodules == 0) { - /* fill in the dyadic extended processors */ - __u8 cpu = i & 0x07; - - printk("Module \"%s\": Dyadic Processor Card\n", - cat_module_name(i)); - voyager_extended_vic_processors |= (1 << cpu); - cpu += 4; - voyager_extended_vic_processors |= (1 << cpu); - outb(VOYAGER_CAT_END, CAT_CMD); - continue; - } - - /* now we want to read the asics on the first submodule, - * which should be the quad base board */ - - cat_read(*modpp, (*modpp)->asic, VOYAGER_SUBMODSELECT, &val); - CDEBUG(("cat_init: SUBMODSELECT value = 0x%x\n", val)); - val = (val & 0x7c) | VOYAGER_QUAD_BASEBOARD; - cat_write(*modpp, (*modpp)->asic, VOYAGER_SUBMODSELECT, val); - - outb(VOYAGER_CAT_END, CAT_CMD); - - CDEBUG(("cat_init: Reading eeprom for module 0x%x at offset %d\n", i, VOYAGER_XSUM_END_OFFSET)); - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_disconnect(*modpp, (*modpp)->asic); - if (cat_subread(*modpp, (*modpp)->asic, - VOYAGER_XSUM_END_OFFSET, sizeof(eprom_size), - &eprom_size)) { - printk - ("**WARNING**: Voyager couldn't read EPROM size for module 0x%x\n", - i); - outb(VOYAGER_CAT_END, CAT_CMD); - continue; - } - if (eprom_size > sizeof(eprom_buf)) { - printk - ("**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d\n", - i, eprom_size); - outb(VOYAGER_CAT_END, CAT_CMD); - continue; - } - outb(VOYAGER_CAT_END, CAT_CMD); - outb(VOYAGER_CAT_RUN, CAT_CMD); - CDEBUG(("cat_init: module 0x%x, eeprom_size %d\n", i, - eprom_size)); - if (cat_subread - (*modpp, (*modpp)->asic, 0, eprom_size, eprom_buf)) { - outb(VOYAGER_CAT_END, CAT_CMD); - continue; - } - outb(VOYAGER_CAT_END, CAT_CMD); - /* Now do everything for the QBB submodule 1 */ - (*modpp)->ee_size = eprom_hdr->ee_size; - (*modpp)->num_asics = eprom_hdr->num_asics; - asicpp = &((*modpp)->asic); - sp_offset = eprom_hdr->scan_path_offset; - /* get rid of the dummy CAT asic and read the real one */ - kfree((*modpp)->asic); - for (asic = 0; asic < (*modpp)->num_asics; asic++) { - int j; - voyager_asic_t *asicp = *asicpp = kzalloc(sizeof(voyager_asic_t), GFP_KERNEL); /*&voyager_asic_storage[asic_count++]; */ - voyager_sp_table_t *sp_table; - voyager_at_t *asic_table; - voyager_jtt_t *jtag_table; - - if (asicp == NULL) { - printk - ("**WARNING** kmalloc failure in cat_init\n"); - continue; - } - asicpp = &(asicp->next); - asicp->asic_location = asic; - sp_table = - (voyager_sp_table_t *) (eprom_buf + sp_offset); - asicp->asic_id = sp_table->asic_id; - asic_table = - (voyager_at_t *) (eprom_buf + - sp_table->asic_data_offset); - for (j = 0; j < 4; j++) - asicp->jtag_id[j] = asic_table->jtag_id[j]; - jtag_table = - (voyager_jtt_t *) (eprom_buf + - asic_table->jtag_offset); - asicp->ireg_length = jtag_table->ireg_len; - asicp->bit_location = (*modpp)->inst_bits; - (*modpp)->inst_bits += asicp->ireg_length; - if (asicp->ireg_length > (*modpp)->largest_reg) - (*modpp)->largest_reg = asicp->ireg_length; - if (asicp->ireg_length < (*modpp)->smallest_reg || - (*modpp)->smallest_reg == 0) - (*modpp)->smallest_reg = asicp->ireg_length; - CDEBUG(("asic 0x%x, ireg_length=%d, bit_location=%d\n", - asicp->asic_id, asicp->ireg_length, - asicp->bit_location)); - if (asicp->asic_id == VOYAGER_QUAD_QABC) { - CDEBUG(("VOYAGER CAT: QABC ASIC found\n")); - qabc_asic = asicp; - } - sp_offset += sizeof(voyager_sp_table_t); - } - CDEBUG(("Module inst_bits = %d, largest_reg = %d, smallest_reg=%d\n", (*modpp)->inst_bits, (*modpp)->largest_reg, (*modpp)->smallest_reg)); - /* OK, now we have the QUAD ASICs set up, use them. - * we need to: - * - * 1. Find the Memory area for the Quad CPIs. - * 2. Find the Extended VIC processor - * 3. Configure a second extended VIC processor (This - * cannot be done for the 51xx. - * */ - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_connect(*modpp, (*modpp)->asic); - CDEBUG(("CAT CONNECTED!!\n")); - cat_subread(*modpp, qabc_asic, 0, sizeof(qabc_data), qabc_data); - qic_addr = qabc_data[5] << 8; - qic_addr = (qic_addr | qabc_data[6]) << 8; - qic_addr = (qic_addr | qabc_data[7]) << 8; - printk - ("Module \"%s\": Quad Processor Card; CPI 0x%lx, SET=0x%x\n", - cat_module_name(i), qic_addr, qabc_data[8]); -#if 0 /* plumbing fails---FIXME */ - if ((qabc_data[8] & 0xf0) == 0) { - /* FIXME: 32 way 8 CPU slot monster cannot be - * plumbed this way---need to check for it */ - - printk("Plumbing second Extended Quad Processor\n"); - /* second VIC line hardwired to Quad CPU 1 */ - qabc_data[8] |= 0x20; - cat_subwrite(*modpp, qabc_asic, 8, 1, &qabc_data[8]); -#ifdef VOYAGER_CAT_DEBUG - /* verify plumbing */ - cat_subread(*modpp, qabc_asic, 8, 1, &qabc_data[8]); - if ((qabc_data[8] & 0xf0) == 0) { - CDEBUG(("PLUMBING FAILED: 0x%x\n", - qabc_data[8])); - } -#endif - } -#endif - - { - struct resource *res = - kzalloc(sizeof(struct resource), GFP_KERNEL); - res->name = kmalloc(128, GFP_KERNEL); - sprintf((char *)res->name, "Voyager %s Quad CPI", - cat_module_name(i)); - res->start = qic_addr; - res->end = qic_addr + 0x3ff; - request_resource(&iomem_resource, res); - } - - qic_addr = (unsigned long)ioremap_cache(qic_addr, 0x400); - - for (j = 0; j < 4; j++) { - __u8 cpu; - - if (voyager_8slot) { - /* 8 slot has a different mapping, - * each slot has only one vic line, so - * 1 cpu in each slot must be < 8 */ - cpu = (i & 0x07) + j * 8; - } else { - cpu = (i & 0x03) + j * 4; - } - if ((qabc_data[8] & (1 << j))) { - voyager_extended_vic_processors |= (1 << cpu); - } - if (qabc_data[8] & (1 << (j + 4))) { - /* Second SET register plumbed: Quad - * card has two VIC connected CPUs. - * Secondary cannot be booted as a VIC - * CPU */ - voyager_extended_vic_processors |= (1 << cpu); - voyager_allowed_boot_processors &= - (~(1 << cpu)); - } - - voyager_quad_processors |= (1 << cpu); - voyager_quad_cpi_addr[cpu] = (struct voyager_qic_cpi *) - (qic_addr + (j << 8)); - CDEBUG(("CPU%d: CPI address 0x%lx\n", cpu, - (unsigned long)voyager_quad_cpi_addr[cpu])); - } - outb(VOYAGER_CAT_END, CAT_CMD); - - *asicpp = NULL; - modpp = &((*modpp)->next); - } - *modpp = NULL; - printk - ("CAT Bus Initialisation finished: extended procs 0x%x, quad procs 0x%x, allowed vic boot = 0x%x\n", - voyager_extended_vic_processors, voyager_quad_processors, - voyager_allowed_boot_processors); - request_resource(&ioport_resource, &vic_res); - if (voyager_quad_processors) - request_resource(&ioport_resource, &qic_res); - /* set up the front power switch */ -} - -int voyager_cat_readb(__u8 module, __u8 asic, int reg) -{ - return 0; -} - -static int cat_disconnect(voyager_module_t * modp, voyager_asic_t * asicp) -{ - __u8 val; - int err = 0; - - if (!modp->scan_path_connected) - return 0; - if (asicp->asic_id != VOYAGER_CAT_ID) { - CDEBUG(("cat_disconnect: ASIC is not CAT\n")); - return 1; - } - err = cat_read(modp, asicp, VOYAGER_SCANPATH, &val); - if (err) { - CDEBUG(("cat_disconnect: failed to read SCANPATH\n")); - return err; - } - val &= VOYAGER_DISCONNECT_ASIC; - err = cat_write(modp, asicp, VOYAGER_SCANPATH, val); - if (err) { - CDEBUG(("cat_disconnect: failed to write SCANPATH\n")); - return err; - } - outb(VOYAGER_CAT_END, CAT_CMD); - outb(VOYAGER_CAT_RUN, CAT_CMD); - modp->scan_path_connected = 0; - - return 0; -} - -static int cat_connect(voyager_module_t * modp, voyager_asic_t * asicp) -{ - __u8 val; - int err = 0; - - if (modp->scan_path_connected) - return 0; - if (asicp->asic_id != VOYAGER_CAT_ID) { - CDEBUG(("cat_connect: ASIC is not CAT\n")); - return 1; - } - - err = cat_read(modp, asicp, VOYAGER_SCANPATH, &val); - if (err) { - CDEBUG(("cat_connect: failed to read SCANPATH\n")); - return err; - } - val |= VOYAGER_CONNECT_ASIC; - err = cat_write(modp, asicp, VOYAGER_SCANPATH, val); - if (err) { - CDEBUG(("cat_connect: failed to write SCANPATH\n")); - return err; - } - outb(VOYAGER_CAT_END, CAT_CMD); - outb(VOYAGER_CAT_RUN, CAT_CMD); - modp->scan_path_connected = 1; - - return 0; -} - -void voyager_cat_power_off(void) -{ - /* Power the machine off by writing to the PSI over the CAT - * bus */ - __u8 data; - voyager_module_t psi = { 0 }; - voyager_asic_t psi_asic = { 0 }; - - psi.asic = &psi_asic; - psi.asic->asic_id = VOYAGER_CAT_ID; - psi.asic->subaddr = VOYAGER_SUBADDR_HI; - psi.module_addr = VOYAGER_PSI; - psi.scan_path_connected = 0; - - outb(VOYAGER_CAT_END, CAT_CMD); - /* Connect the PSI to the CAT Bus */ - outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); - outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT); - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_disconnect(&psi, &psi_asic); - /* Read the status */ - cat_subread(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data); - outb(VOYAGER_CAT_END, CAT_CMD); - CDEBUG(("PSI STATUS 0x%x\n", data)); - /* These two writes are power off prep and perform */ - data = PSI_CLEAR; - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data); - outb(VOYAGER_CAT_END, CAT_CMD); - data = PSI_POWER_DOWN; - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data); - outb(VOYAGER_CAT_END, CAT_CMD); -} - -struct voyager_status voyager_status = { 0 }; - -void voyager_cat_psi(__u8 cmd, __u16 reg, __u8 * data) -{ - voyager_module_t psi = { 0 }; - voyager_asic_t psi_asic = { 0 }; - - psi.asic = &psi_asic; - psi.asic->asic_id = VOYAGER_CAT_ID; - psi.asic->subaddr = VOYAGER_SUBADDR_HI; - psi.module_addr = VOYAGER_PSI; - psi.scan_path_connected = 0; - - outb(VOYAGER_CAT_END, CAT_CMD); - /* Connect the PSI to the CAT Bus */ - outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); - outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT); - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_disconnect(&psi, &psi_asic); - switch (cmd) { - case VOYAGER_PSI_READ: - cat_read(&psi, &psi_asic, reg, data); - break; - case VOYAGER_PSI_WRITE: - cat_write(&psi, &psi_asic, reg, *data); - break; - case VOYAGER_PSI_SUBREAD: - cat_subread(&psi, &psi_asic, reg, 1, data); - break; - case VOYAGER_PSI_SUBWRITE: - cat_subwrite(&psi, &psi_asic, reg, 1, data); - break; - default: - printk(KERN_ERR "Voyager PSI, unrecognised command %d\n", cmd); - break; - } - outb(VOYAGER_CAT_END, CAT_CMD); -} - -void voyager_cat_do_common_interrupt(void) -{ - /* This is caused either by a memory parity error or something - * in the PSI */ - __u8 data; - voyager_module_t psi = { 0 }; - voyager_asic_t psi_asic = { 0 }; - struct voyager_psi psi_reg; - int i; - re_read: - psi.asic = &psi_asic; - psi.asic->asic_id = VOYAGER_CAT_ID; - psi.asic->subaddr = VOYAGER_SUBADDR_HI; - psi.module_addr = VOYAGER_PSI; - psi.scan_path_connected = 0; - - outb(VOYAGER_CAT_END, CAT_CMD); - /* Connect the PSI to the CAT Bus */ - outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); - outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT); - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_disconnect(&psi, &psi_asic); - /* Read the status. NOTE: Need to read *all* the PSI regs here - * otherwise the cmn int will be reasserted */ - for (i = 0; i < sizeof(psi_reg.regs); i++) { - cat_read(&psi, &psi_asic, i, &((__u8 *) & psi_reg.regs)[i]); - } - outb(VOYAGER_CAT_END, CAT_CMD); - if ((psi_reg.regs.checkbit & 0x02) == 0) { - psi_reg.regs.checkbit |= 0x02; - cat_write(&psi, &psi_asic, 5, psi_reg.regs.checkbit); - printk("VOYAGER RE-READ PSI\n"); - goto re_read; - } - outb(VOYAGER_CAT_RUN, CAT_CMD); - for (i = 0; i < sizeof(psi_reg.subregs); i++) { - /* This looks strange, but the PSI doesn't do auto increment - * correctly */ - cat_subread(&psi, &psi_asic, VOYAGER_PSI_SUPPLY_REG + i, - 1, &((__u8 *) & psi_reg.subregs)[i]); - } - outb(VOYAGER_CAT_END, CAT_CMD); -#ifdef VOYAGER_CAT_DEBUG - printk("VOYAGER PSI: "); - for (i = 0; i < sizeof(psi_reg.regs); i++) - printk("%02x ", ((__u8 *) & psi_reg.regs)[i]); - printk("\n "); - for (i = 0; i < sizeof(psi_reg.subregs); i++) - printk("%02x ", ((__u8 *) & psi_reg.subregs)[i]); - printk("\n"); -#endif - if (psi_reg.regs.intstatus & PSI_MON) { - /* switch off or power fail */ - - if (psi_reg.subregs.supply & PSI_SWITCH_OFF) { - if (voyager_status.switch_off) { - printk(KERN_ERR - "Voyager front panel switch turned off again---Immediate power off!\n"); - voyager_cat_power_off(); - /* not reached */ - } else { - printk(KERN_ERR - "Voyager front panel switch turned off\n"); - voyager_status.switch_off = 1; - voyager_status.request_from_kernel = 1; - wake_up_process(voyager_thread); - } - /* Tell the hardware we're taking care of the - * shutdown, otherwise it will power the box off - * within 3 seconds of the switch being pressed and, - * which is much more important to us, continue to - * assert the common interrupt */ - data = PSI_CLR_SWITCH_OFF; - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_SUPPLY_REG, - 1, &data); - outb(VOYAGER_CAT_END, CAT_CMD); - } else { - - VDEBUG(("Voyager ac fail reg 0x%x\n", - psi_reg.subregs.ACfail)); - if ((psi_reg.subregs.ACfail & AC_FAIL_STAT_CHANGE) == 0) { - /* No further update */ - return; - } -#if 0 - /* Don't bother trying to find out who failed. - * FIXME: This probably makes the code incorrect on - * anything other than a 345x */ - for (i = 0; i < 5; i++) { - if (psi_reg.subregs.ACfail & (1 << i)) { - break; - } - } - printk(KERN_NOTICE "AC FAIL IN SUPPLY %d\n", i); -#endif - /* DON'T do this: it shuts down the AC PSI - outb(VOYAGER_CAT_RUN, CAT_CMD); - data = PSI_MASK_MASK | i; - cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_MASK, - 1, &data); - outb(VOYAGER_CAT_END, CAT_CMD); - */ - printk(KERN_ERR "Voyager AC power failure\n"); - outb(VOYAGER_CAT_RUN, CAT_CMD); - data = PSI_COLD_START; - cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, - 1, &data); - outb(VOYAGER_CAT_END, CAT_CMD); - voyager_status.power_fail = 1; - voyager_status.request_from_kernel = 1; - wake_up_process(voyager_thread); - } - - } else if (psi_reg.regs.intstatus & PSI_FAULT) { - /* Major fault! */ - printk(KERN_ERR - "Voyager PSI Detected major fault, immediate power off!\n"); - voyager_cat_power_off(); - /* not reached */ - } else if (psi_reg.regs.intstatus & (PSI_DC_FAIL | PSI_ALARM - | PSI_CURRENT | PSI_DVM - | PSI_PSCFAULT | PSI_STAT_CHG)) { - /* other psi fault */ - - printk(KERN_WARNING "Voyager PSI status 0x%x\n", data); - /* clear the PSI fault */ - outb(VOYAGER_CAT_RUN, CAT_CMD); - cat_write(&psi, &psi_asic, VOYAGER_PSI_STATUS_REG, 0); - outb(VOYAGER_CAT_END, CAT_CMD); - } -} diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c deleted file mode 100644 index 98e3c2bc7563..000000000000 --- a/arch/x86/mach-voyager/voyager_smp.c +++ /dev/null @@ -1,1805 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8 -*- */ - -/* Copyright (C) 1999,2001 - * - * Author: J.E.J.Bottomley@HansenPartnership.com - * - * This file provides all the same external entries as smp.c but uses - * the voyager hal to provide the functionality - */ -#include <linux/cpu.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/kernel_stat.h> -#include <linux/delay.h> -#include <linux/mc146818rtc.h> -#include <linux/cache.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/bootmem.h> -#include <linux/completion.h> -#include <asm/desc.h> -#include <asm/voyager.h> -#include <asm/vic.h> -#include <asm/mtrr.h> -#include <asm/pgalloc.h> -#include <asm/tlbflush.h> -#include <asm/arch_hooks.h> -#include <asm/trampoline.h> - -/* TLB state -- visible externally, indexed physically */ -DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm, 0 }; - -/* CPU IRQ affinity -- set to all ones initially */ -static unsigned long cpu_irq_affinity[NR_CPUS] __cacheline_aligned = - {[0 ... NR_CPUS-1] = ~0UL }; - -/* per CPU data structure (for /proc/cpuinfo et al), visible externally - * indexed physically */ -DEFINE_PER_CPU_SHARED_ALIGNED(struct cpuinfo_x86, cpu_info); -EXPORT_PER_CPU_SYMBOL(cpu_info); - -/* physical ID of the CPU used to boot the system */ -unsigned char boot_cpu_id; - -/* The memory line addresses for the Quad CPIs */ -struct voyager_qic_cpi *voyager_quad_cpi_addr[NR_CPUS] __cacheline_aligned; - -/* The masks for the Extended VIC processors, filled in by cat_init */ -__u32 voyager_extended_vic_processors = 0; - -/* Masks for the extended Quad processors which cannot be VIC booted */ -__u32 voyager_allowed_boot_processors = 0; - -/* The mask for the Quad Processors (both extended and non-extended) */ -__u32 voyager_quad_processors = 0; - -/* Total count of live CPUs, used in process.c to display - * the CPU information and in irq.c for the per CPU irq - * activity count. Finally exported by i386_ksyms.c */ -static int voyager_extended_cpus = 1; - -/* Used for the invalidate map that's also checked in the spinlock */ -static volatile unsigned long smp_invalidate_needed; - -/* Bitmask of CPUs present in the system - exported by i386_syms.c, used - * by scheduler but indexed physically */ -cpumask_t phys_cpu_present_map = CPU_MASK_NONE; - -/* The internal functions */ -static void send_CPI(__u32 cpuset, __u8 cpi); -static void ack_CPI(__u8 cpi); -static int ack_QIC_CPI(__u8 cpi); -static void ack_special_QIC_CPI(__u8 cpi); -static void ack_VIC_CPI(__u8 cpi); -static void send_CPI_allbutself(__u8 cpi); -static void mask_vic_irq(unsigned int irq); -static void unmask_vic_irq(unsigned int irq); -static unsigned int startup_vic_irq(unsigned int irq); -static void enable_local_vic_irq(unsigned int irq); -static void disable_local_vic_irq(unsigned int irq); -static void before_handle_vic_irq(unsigned int irq); -static void after_handle_vic_irq(unsigned int irq); -static void set_vic_irq_affinity(unsigned int irq, const struct cpumask *mask); -static void ack_vic_irq(unsigned int irq); -static void vic_enable_cpi(void); -static void do_boot_cpu(__u8 cpuid); -static void do_quad_bootstrap(void); -static void initialize_secondary(void); - -int hard_smp_processor_id(void); -int safe_smp_processor_id(void); - -/* Inline functions */ -static inline void send_one_QIC_CPI(__u8 cpu, __u8 cpi) -{ - voyager_quad_cpi_addr[cpu]->qic_cpi[cpi].cpi = - (smp_processor_id() << 16) + cpi; -} - -static inline void send_QIC_CPI(__u32 cpuset, __u8 cpi) -{ - int cpu; - - for_each_online_cpu(cpu) { - if (cpuset & (1 << cpu)) { -#ifdef VOYAGER_DEBUG - if (!cpu_online(cpu)) - VDEBUG(("CPU%d sending cpi %d to CPU%d not in " - "cpu_online_map\n", - hard_smp_processor_id(), cpi, cpu)); -#endif - send_one_QIC_CPI(cpu, cpi - QIC_CPI_OFFSET); - } - } -} - -static inline void wrapper_smp_local_timer_interrupt(void) -{ - irq_enter(); - smp_local_timer_interrupt(); - irq_exit(); -} - -static inline void send_one_CPI(__u8 cpu, __u8 cpi) -{ - if (voyager_quad_processors & (1 << cpu)) - send_one_QIC_CPI(cpu, cpi - QIC_CPI_OFFSET); - else - send_CPI(1 << cpu, cpi); -} - -static inline void send_CPI_allbutself(__u8 cpi) -{ - __u8 cpu = smp_processor_id(); - __u32 mask = cpus_addr(cpu_online_map)[0] & ~(1 << cpu); - send_CPI(mask, cpi); -} - -static inline int is_cpu_quad(void) -{ - __u8 cpumask = inb(VIC_PROC_WHO_AM_I); - return ((cpumask & QUAD_IDENTIFIER) == QUAD_IDENTIFIER); -} - -static inline int is_cpu_extended(void) -{ - __u8 cpu = hard_smp_processor_id(); - - return (voyager_extended_vic_processors & (1 << cpu)); -} - -static inline int is_cpu_vic_boot(void) -{ - __u8 cpu = hard_smp_processor_id(); - - return (voyager_extended_vic_processors - & voyager_allowed_boot_processors & (1 << cpu)); -} - -static inline void ack_CPI(__u8 cpi) -{ - switch (cpi) { - case VIC_CPU_BOOT_CPI: - if (is_cpu_quad() && !is_cpu_vic_boot()) - ack_QIC_CPI(cpi); - else - ack_VIC_CPI(cpi); - break; - case VIC_SYS_INT: - case VIC_CMN_INT: - /* These are slightly strange. Even on the Quad card, - * They are vectored as VIC CPIs */ - if (is_cpu_quad()) - ack_special_QIC_CPI(cpi); - else - ack_VIC_CPI(cpi); - break; - default: - printk("VOYAGER ERROR: CPI%d is in common CPI code\n", cpi); - break; - } -} - -/* local variables */ - -/* The VIC IRQ descriptors -- these look almost identical to the - * 8259 IRQs except that masks and things must be kept per processor - */ -static struct irq_chip vic_chip = { - .name = "VIC", - .startup = startup_vic_irq, - .mask = mask_vic_irq, - .unmask = unmask_vic_irq, - .set_affinity = set_vic_irq_affinity, -}; - -/* used to count up as CPUs are brought on line (starts at 0) */ -static int cpucount = 0; - -/* The per cpu profile stuff - used in smp_local_timer_interrupt */ -static DEFINE_PER_CPU(int, prof_multiplier) = 1; -static DEFINE_PER_CPU(int, prof_old_multiplier) = 1; -static DEFINE_PER_CPU(int, prof_counter) = 1; - -/* the map used to check if a CPU has booted */ -static __u32 cpu_booted_map; - -/* the synchronize flag used to hold all secondary CPUs spinning in - * a tight loop until the boot sequence is ready for them */ -static cpumask_t smp_commenced_mask = CPU_MASK_NONE; - -/* This is for the new dynamic CPU boot code */ - -/* The per processor IRQ masks (these are usually kept in sync) */ -static __u16 vic_irq_mask[NR_CPUS] __cacheline_aligned; - -/* the list of IRQs to be enabled by the VIC_ENABLE_IRQ_CPI */ -static __u16 vic_irq_enable_mask[NR_CPUS] __cacheline_aligned = { 0 }; - -/* Lock for enable/disable of VIC interrupts */ -static __cacheline_aligned DEFINE_SPINLOCK(vic_irq_lock); - -/* The boot processor is correctly set up in PC mode when it - * comes up, but the secondaries need their master/slave 8259 - * pairs initializing correctly */ - -/* Interrupt counters (per cpu) and total - used to try to - * even up the interrupt handling routines */ -static long vic_intr_total = 0; -static long vic_intr_count[NR_CPUS] __cacheline_aligned = { 0 }; -static unsigned long vic_tick[NR_CPUS] __cacheline_aligned = { 0 }; - -/* Since we can only use CPI0, we fake all the other CPIs */ -static unsigned long vic_cpi_mailbox[NR_CPUS] __cacheline_aligned; - -/* debugging routine to read the isr of the cpu's pic */ -static inline __u16 vic_read_isr(void) -{ - __u16 isr; - - outb(0x0b, 0xa0); - isr = inb(0xa0) << 8; - outb(0x0b, 0x20); - isr |= inb(0x20); - - return isr; -} - -static __init void qic_setup(void) -{ - if (!is_cpu_quad()) { - /* not a quad, no setup */ - return; - } - outb(QIC_DEFAULT_MASK0, QIC_MASK_REGISTER0); - outb(QIC_CPI_ENABLE, QIC_MASK_REGISTER1); - - if (is_cpu_extended()) { - /* the QIC duplicate of the VIC base register */ - outb(VIC_DEFAULT_CPI_BASE, QIC_VIC_CPI_BASE_REGISTER); - outb(QIC_DEFAULT_CPI_BASE, QIC_CPI_BASE_REGISTER); - - /* FIXME: should set up the QIC timer and memory parity - * error vectors here */ - } -} - -static __init void vic_setup_pic(void) -{ - outb(1, VIC_REDIRECT_REGISTER_1); - /* clear the claim registers for dynamic routing */ - outb(0, VIC_CLAIM_REGISTER_0); - outb(0, VIC_CLAIM_REGISTER_1); - - outb(0, VIC_PRIORITY_REGISTER); - /* Set the Primary and Secondary Microchannel vector - * bases to be the same as the ordinary interrupts - * - * FIXME: This would be more efficient using separate - * vectors. */ - outb(FIRST_EXTERNAL_VECTOR, VIC_PRIMARY_MC_BASE); - outb(FIRST_EXTERNAL_VECTOR, VIC_SECONDARY_MC_BASE); - /* Now initiallise the master PIC belonging to this CPU by - * sending the four ICWs */ - - /* ICW1: level triggered, ICW4 needed */ - outb(0x19, 0x20); - - /* ICW2: vector base */ - outb(FIRST_EXTERNAL_VECTOR, 0x21); - - /* ICW3: slave at line 2 */ - outb(0x04, 0x21); - - /* ICW4: 8086 mode */ - outb(0x01, 0x21); - - /* now the same for the slave PIC */ - - /* ICW1: level trigger, ICW4 needed */ - outb(0x19, 0xA0); - - /* ICW2: slave vector base */ - outb(FIRST_EXTERNAL_VECTOR + 8, 0xA1); - - /* ICW3: slave ID */ - outb(0x02, 0xA1); - - /* ICW4: 8086 mode */ - outb(0x01, 0xA1); -} - -static void do_quad_bootstrap(void) -{ - if (is_cpu_quad() && is_cpu_vic_boot()) { - int i; - unsigned long flags; - __u8 cpuid = hard_smp_processor_id(); - - local_irq_save(flags); - - for (i = 0; i < 4; i++) { - /* FIXME: this would be >>3 &0x7 on the 32 way */ - if (((cpuid >> 2) & 0x03) == i) - /* don't lower our own mask! */ - continue; - - /* masquerade as local Quad CPU */ - outb(QIC_CPUID_ENABLE | i, QIC_PROCESSOR_ID); - /* enable the startup CPI */ - outb(QIC_BOOT_CPI_MASK, QIC_MASK_REGISTER1); - /* restore cpu id */ - outb(0, QIC_PROCESSOR_ID); - } - local_irq_restore(flags); - } -} - -void prefill_possible_map(void) -{ - /* This is empty on voyager because we need a much - * earlier detection which is done in find_smp_config */ -} - -/* Set up all the basic stuff: read the SMP config and make all the - * SMP information reflect only the boot cpu. All others will be - * brought on-line later. */ -void __init find_smp_config(void) -{ - int i; - - boot_cpu_id = hard_smp_processor_id(); - - printk("VOYAGER SMP: Boot cpu is %d\n", boot_cpu_id); - - /* initialize the CPU structures (moved from smp_boot_cpus) */ - for (i = 0; i < nr_cpu_ids; i++) - cpu_irq_affinity[i] = ~0; - cpu_online_map = cpumask_of_cpu(boot_cpu_id); - - /* The boot CPU must be extended */ - voyager_extended_vic_processors = 1 << boot_cpu_id; - /* initially, all of the first 8 CPUs can boot */ - voyager_allowed_boot_processors = 0xff; - /* set up everything for just this CPU, we can alter - * this as we start the other CPUs later */ - /* now get the CPU disposition from the extended CMOS */ - cpus_addr(phys_cpu_present_map)[0] = - voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK); - cpus_addr(phys_cpu_present_map)[0] |= - voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + 1) << 8; - cpus_addr(phys_cpu_present_map)[0] |= - voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + - 2) << 16; - cpus_addr(phys_cpu_present_map)[0] |= - voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + - 3) << 24; - init_cpu_possible(&phys_cpu_present_map); - printk("VOYAGER SMP: phys_cpu_present_map = 0x%lx\n", - cpus_addr(phys_cpu_present_map)[0]); - /* Here we set up the VIC to enable SMP */ - /* enable the CPIs by writing the base vector to their register */ - outb(VIC_DEFAULT_CPI_BASE, VIC_CPI_BASE_REGISTER); - outb(1, VIC_REDIRECT_REGISTER_1); - /* set the claim registers for static routing --- Boot CPU gets - * all interrupts untill all other CPUs started */ - outb(0xff, VIC_CLAIM_REGISTER_0); - outb(0xff, VIC_CLAIM_REGISTER_1); - /* Set the Primary and Secondary Microchannel vector - * bases to be the same as the ordinary interrupts - * - * FIXME: This would be more efficient using separate - * vectors. */ - outb(FIRST_EXTERNAL_VECTOR, VIC_PRIMARY_MC_BASE); - outb(FIRST_EXTERNAL_VECTOR, VIC_SECONDARY_MC_BASE); - - /* Finally tell the firmware that we're driving */ - outb(inb(VOYAGER_SUS_IN_CONTROL_PORT) | VOYAGER_IN_CONTROL_FLAG, - VOYAGER_SUS_IN_CONTROL_PORT); - - current_thread_info()->cpu = boot_cpu_id; - percpu_write(cpu_number, boot_cpu_id); -} - -/* - * The bootstrap kernel entry code has set these up. Save them - * for a given CPU, id is physical */ -void __init smp_store_cpu_info(int id) -{ - struct cpuinfo_x86 *c = &cpu_data(id); - - *c = boot_cpu_data; - c->cpu_index = id; - - identify_secondary_cpu(c); -} - -/* Routine initially called when a non-boot CPU is brought online */ -static void __init start_secondary(void *unused) -{ - __u8 cpuid = hard_smp_processor_id(); - - cpu_init(); - - /* OK, we're in the routine */ - ack_CPI(VIC_CPU_BOOT_CPI); - - /* setup the 8259 master slave pair belonging to this CPU --- - * we won't actually receive any until the boot CPU - * relinquishes it's static routing mask */ - vic_setup_pic(); - - qic_setup(); - - if (is_cpu_quad() && !is_cpu_vic_boot()) { - /* clear the boot CPI */ - __u8 dummy; - - dummy = - voyager_quad_cpi_addr[cpuid]->qic_cpi[VIC_CPU_BOOT_CPI].cpi; - printk("read dummy %d\n", dummy); - } - - /* lower the mask to receive CPIs */ - vic_enable_cpi(); - - VDEBUG(("VOYAGER SMP: CPU%d, stack at about %p\n", cpuid, &cpuid)); - - notify_cpu_starting(cpuid); - - /* enable interrupts */ - local_irq_enable(); - - /* get our bogomips */ - calibrate_delay(); - - /* save our processor parameters */ - smp_store_cpu_info(cpuid); - - /* if we're a quad, we may need to bootstrap other CPUs */ - do_quad_bootstrap(); - - /* FIXME: this is rather a poor hack to prevent the CPU - * activating softirqs while it's supposed to be waiting for - * permission to proceed. Without this, the new per CPU stuff - * in the softirqs will fail */ - local_irq_disable(); - cpu_set(cpuid, cpu_callin_map); - - /* signal that we're done */ - cpu_booted_map = 1; - - while (!cpu_isset(cpuid, smp_commenced_mask)) - rep_nop(); - local_irq_enable(); - - local_flush_tlb(); - - cpu_set(cpuid, cpu_online_map); - wmb(); - cpu_idle(); -} - -/* Routine to kick start the given CPU and wait for it to report ready - * (or timeout in startup). When this routine returns, the requested - * CPU is either fully running and configured or known to be dead. - * - * We call this routine sequentially 1 CPU at a time, so no need for - * locking */ - -static void __init do_boot_cpu(__u8 cpu) -{ - struct task_struct *idle; - int timeout; - unsigned long flags; - int quad_boot = (1 << cpu) & voyager_quad_processors - & ~(voyager_extended_vic_processors - & voyager_allowed_boot_processors); - - /* This is the format of the CPI IDT gate (in real mode) which - * we're hijacking to boot the CPU */ - union IDTFormat { - struct seg { - __u16 Offset; - __u16 Segment; - } idt; - __u32 val; - } hijack_source; - - __u32 *hijack_vector; - __u32 start_phys_address = setup_trampoline(); - - /* There's a clever trick to this: The linux trampoline is - * compiled to begin at absolute location zero, so make the - * address zero but have the data segment selector compensate - * for the actual address */ - hijack_source.idt.Offset = start_phys_address & 0x000F; - hijack_source.idt.Segment = (start_phys_address >> 4) & 0xFFFF; - - cpucount++; - alternatives_smp_switch(1); - - idle = fork_idle(cpu); - if (IS_ERR(idle)) - panic("failed fork for CPU%d", cpu); - idle->thread.ip = (unsigned long)start_secondary; - /* init_tasks (in sched.c) is indexed logically */ - stack_start.sp = (void *)idle->thread.sp; - - per_cpu(current_task, cpu) = idle; - early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu); - irq_ctx_init(cpu); - - /* Note: Don't modify initial ss override */ - VDEBUG(("VOYAGER SMP: Booting CPU%d at 0x%lx[%x:%x], stack %p\n", cpu, - (unsigned long)hijack_source.val, hijack_source.idt.Segment, - hijack_source.idt.Offset, stack_start.sp)); - - /* init lowmem identity mapping */ - clone_pgd_range(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY, - min_t(unsigned long, KERNEL_PGD_PTRS, KERNEL_PGD_BOUNDARY)); - flush_tlb_all(); - - if (quad_boot) { - printk("CPU %d: non extended Quad boot\n", cpu); - hijack_vector = - (__u32 *) - phys_to_virt((VIC_CPU_BOOT_CPI + QIC_DEFAULT_CPI_BASE) * 4); - *hijack_vector = hijack_source.val; - } else { - printk("CPU%d: extended VIC boot\n", cpu); - hijack_vector = - (__u32 *) - phys_to_virt((VIC_CPU_BOOT_CPI + VIC_DEFAULT_CPI_BASE) * 4); - *hijack_vector = hijack_source.val; - /* VIC errata, may also receive interrupt at this address */ - hijack_vector = - (__u32 *) - phys_to_virt((VIC_CPU_BOOT_ERRATA_CPI + - VIC_DEFAULT_CPI_BASE) * 4); - *hijack_vector = hijack_source.val; - } - /* All non-boot CPUs start with interrupts fully masked. Need - * to lower the mask of the CPI we're about to send. We do - * this in the VIC by masquerading as the processor we're - * about to boot and lowering its interrupt mask */ - local_irq_save(flags); - if (quad_boot) { - send_one_QIC_CPI(cpu, VIC_CPU_BOOT_CPI); - } else { - outb(VIC_CPU_MASQUERADE_ENABLE | cpu, VIC_PROCESSOR_ID); - /* here we're altering registers belonging to `cpu' */ - - outb(VIC_BOOT_INTERRUPT_MASK, 0x21); - /* now go back to our original identity */ - outb(boot_cpu_id, VIC_PROCESSOR_ID); - - /* and boot the CPU */ - - send_CPI((1 << cpu), VIC_CPU_BOOT_CPI); - } - cpu_booted_map = 0; - local_irq_restore(flags); - - /* now wait for it to become ready (or timeout) */ - for (timeout = 0; timeout < 50000; timeout++) { - if (cpu_booted_map) - break; - udelay(100); - } - /* reset the page table */ - zap_low_mappings(); - - if (cpu_booted_map) { - VDEBUG(("CPU%d: Booted successfully, back in CPU %d\n", - cpu, smp_processor_id())); - - printk("CPU%d: ", cpu); - print_cpu_info(&cpu_data(cpu)); - wmb(); - cpu_set(cpu, cpu_callout_map); - cpu_set(cpu, cpu_present_map); - } else { - printk("CPU%d FAILED TO BOOT: ", cpu); - if (* - ((volatile unsigned char *)phys_to_virt(start_phys_address)) - == 0xA5) - printk("Stuck.\n"); - else - printk("Not responding.\n"); - - cpucount--; - } -} - -void __init smp_boot_cpus(void) -{ - int i; - - /* CAT BUS initialisation must be done after the memory */ - /* FIXME: The L4 has a catbus too, it just needs to be - * accessed in a totally different way */ - if (voyager_level == 5) { - voyager_cat_init(); - - /* now that the cat has probed the Voyager System Bus, sanity - * check the cpu map */ - if (((voyager_quad_processors | voyager_extended_vic_processors) - & cpus_addr(phys_cpu_present_map)[0]) != - cpus_addr(phys_cpu_present_map)[0]) { - /* should panic */ - printk("\n\n***WARNING*** " - "Sanity check of CPU present map FAILED\n"); - } - } else if (voyager_level == 4) - voyager_extended_vic_processors = - cpus_addr(phys_cpu_present_map)[0]; - - /* this sets up the idle task to run on the current cpu */ - voyager_extended_cpus = 1; - /* Remove the global_irq_holder setting, it triggers a BUG() on - * schedule at the moment */ - //global_irq_holder = boot_cpu_id; - - /* FIXME: Need to do something about this but currently only works - * on CPUs with a tsc which none of mine have. - smp_tune_scheduling(); - */ - smp_store_cpu_info(boot_cpu_id); - /* setup the jump vector */ - initial_code = (unsigned long)initialize_secondary; - printk("CPU%d: ", boot_cpu_id); - print_cpu_info(&cpu_data(boot_cpu_id)); - - if (is_cpu_quad()) { - /* booting on a Quad CPU */ - printk("VOYAGER SMP: Boot CPU is Quad\n"); - qic_setup(); - do_quad_bootstrap(); - } - - /* enable our own CPIs */ - vic_enable_cpi(); - - cpu_set(boot_cpu_id, cpu_online_map); - cpu_set(boot_cpu_id, cpu_callout_map); - - /* loop over all the extended VIC CPUs and boot them. The - * Quad CPUs must be bootstrapped by their extended VIC cpu */ - for (i = 0; i < nr_cpu_ids; i++) { - if (i == boot_cpu_id || !cpu_isset(i, phys_cpu_present_map)) - continue; - do_boot_cpu(i); - /* This udelay seems to be needed for the Quad boots - * don't remove unless you know what you're doing */ - udelay(1000); - } - /* we could compute the total bogomips here, but why bother?, - * Code added from smpboot.c */ - { - unsigned long bogosum = 0; - - for_each_online_cpu(i) - bogosum += cpu_data(i).loops_per_jiffy; - printk(KERN_INFO "Total of %d processors activated " - "(%lu.%02lu BogoMIPS).\n", - cpucount + 1, bogosum / (500000 / HZ), - (bogosum / (5000 / HZ)) % 100); - } - voyager_extended_cpus = hweight32(voyager_extended_vic_processors); - printk("VOYAGER: Extended (interrupt handling CPUs): " - "%d, non-extended: %d\n", voyager_extended_cpus, - num_booting_cpus() - voyager_extended_cpus); - /* that's it, switch to symmetric mode */ - outb(0, VIC_PRIORITY_REGISTER); - outb(0, VIC_CLAIM_REGISTER_0); - outb(0, VIC_CLAIM_REGISTER_1); - - VDEBUG(("VOYAGER SMP: Booted with %d CPUs\n", num_booting_cpus())); -} - -/* Reload the secondary CPUs task structure (this function does not - * return ) */ -static void __init initialize_secondary(void) -{ -#if 0 - // AC kernels only - set_current(hard_get_current()); -#endif - - /* - * We don't actually need to load the full TSS, - * basically just the stack pointer and the eip. - */ - - asm volatile ("movl %0,%%esp\n\t" - "jmp *%1"::"r" (current->thread.sp), - "r"(current->thread.ip)); -} - -/* handle a Voyager SYS_INT -- If we don't, the base board will - * panic the system. - * - * System interrupts occur because some problem was detected on the - * various busses. To find out what you have to probe all the - * hardware via the CAT bus. FIXME: At the moment we do nothing. */ -void smp_vic_sys_interrupt(struct pt_regs *regs) -{ - ack_CPI(VIC_SYS_INT); - printk("Voyager SYSTEM INTERRUPT\n"); -} - -/* Handle a voyager CMN_INT; These interrupts occur either because of - * a system status change or because a single bit memory error - * occurred. FIXME: At the moment, ignore all this. */ -void smp_vic_cmn_interrupt(struct pt_regs *regs) -{ - static __u8 in_cmn_int = 0; - static DEFINE_SPINLOCK(cmn_int_lock); - - /* common ints are broadcast, so make sure we only do this once */ - _raw_spin_lock(&cmn_int_lock); - if (in_cmn_int) - goto unlock_end; - - in_cmn_int++; - _raw_spin_unlock(&cmn_int_lock); - - VDEBUG(("Voyager COMMON INTERRUPT\n")); - - if (voyager_level == 5) - voyager_cat_do_common_interrupt(); - - _raw_spin_lock(&cmn_int_lock); - in_cmn_int = 0; - unlock_end: - _raw_spin_unlock(&cmn_int_lock); - ack_CPI(VIC_CMN_INT); -} - -/* - * Reschedule call back. Nothing to do, all the work is done - * automatically when we return from the interrupt. */ -static void smp_reschedule_interrupt(void) -{ - /* do nothing */ -} - -static struct mm_struct *flush_mm; -static unsigned long flush_va; -static DEFINE_SPINLOCK(tlbstate_lock); - -/* - * We cannot call mmdrop() because we are in interrupt context, - * instead update mm->cpu_vm_mask. - * - * We need to reload %cr3 since the page tables may be going - * away from under us.. - */ -static inline void voyager_leave_mm(unsigned long cpu) -{ - if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK) - BUG(); - cpu_clear(cpu, per_cpu(cpu_tlbstate, cpu).active_mm->cpu_vm_mask); - load_cr3(swapper_pg_dir); -} - -/* - * Invalidate call-back - */ -static void smp_invalidate_interrupt(void) -{ - __u8 cpu = smp_processor_id(); - - if (!test_bit(cpu, &smp_invalidate_needed)) - return; - /* This will flood messages. Don't uncomment unless you see - * Problems with cross cpu invalidation - VDEBUG(("VOYAGER SMP: CPU%d received INVALIDATE_CPI\n", - smp_processor_id())); - */ - - if (flush_mm == per_cpu(cpu_tlbstate, cpu).active_mm) { - if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK) { - if (flush_va == TLB_FLUSH_ALL) - local_flush_tlb(); - else - __flush_tlb_one(flush_va); - } else - voyager_leave_mm(cpu); - } - smp_mb__before_clear_bit(); - clear_bit(cpu, &smp_invalidate_needed); - smp_mb__after_clear_bit(); -} - -/* All the new flush operations for 2.4 */ - -/* This routine is called with a physical cpu mask */ -static void -voyager_flush_tlb_others(unsigned long cpumask, struct mm_struct *mm, - unsigned long va) -{ - int stuck = 50000; - - if (!cpumask) - BUG(); - if ((cpumask & cpus_addr(cpu_online_map)[0]) != cpumask) - BUG(); - if (cpumask & (1 << smp_processor_id())) - BUG(); - if (!mm) - BUG(); - - spin_lock(&tlbstate_lock); - - flush_mm = mm; - flush_va = va; - atomic_set_mask(cpumask, &smp_invalidate_needed); - /* - * We have to send the CPI only to - * CPUs affected. - */ - send_CPI(cpumask, VIC_INVALIDATE_CPI); - - while (smp_invalidate_needed) { - mb(); - if (--stuck == 0) { - printk("***WARNING*** Stuck doing invalidate CPI " - "(CPU%d)\n", smp_processor_id()); - break; - } - } - - /* Uncomment only to debug invalidation problems - VDEBUG(("VOYAGER SMP: Completed invalidate CPI (CPU%d)\n", cpu)); - */ - - flush_mm = NULL; - flush_va = 0; - spin_unlock(&tlbstate_lock); -} - -void flush_tlb_current_task(void) -{ - struct mm_struct *mm = current->mm; - unsigned long cpu_mask; - - preempt_disable(); - - cpu_mask = cpus_addr(mm->cpu_vm_mask)[0] & ~(1 << smp_processor_id()); - local_flush_tlb(); - if (cpu_mask) - voyager_flush_tlb_others(cpu_mask, mm, TLB_FLUSH_ALL); - - preempt_enable(); -} - -void flush_tlb_mm(struct mm_struct *mm) -{ - unsigned long cpu_mask; - - preempt_disable(); - - cpu_mask = cpus_addr(mm->cpu_vm_mask)[0] & ~(1 << smp_processor_id()); - - if (current->active_mm == mm) { - if (current->mm) - local_flush_tlb(); - else - voyager_leave_mm(smp_processor_id()); - } - if (cpu_mask) - voyager_flush_tlb_others(cpu_mask, mm, TLB_FLUSH_ALL); - - preempt_enable(); -} - -void flush_tlb_page(struct vm_area_struct *vma, unsigned long va) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long cpu_mask; - - preempt_disable(); - - cpu_mask = cpus_addr(mm->cpu_vm_mask)[0] & ~(1 << smp_processor_id()); - if (current->active_mm == mm) { - if (current->mm) - __flush_tlb_one(va); - else - voyager_leave_mm(smp_processor_id()); - } - - if (cpu_mask) - voyager_flush_tlb_others(cpu_mask, mm, va); - - preempt_enable(); -} - -EXPORT_SYMBOL(flush_tlb_page); - -/* enable the requested IRQs */ -static void smp_enable_irq_interrupt(void) -{ - __u8 irq; - __u8 cpu = get_cpu(); - - VDEBUG(("VOYAGER SMP: CPU%d enabling irq mask 0x%x\n", cpu, - vic_irq_enable_mask[cpu])); - - spin_lock(&vic_irq_lock); - for (irq = 0; irq < 16; irq++) { - if (vic_irq_enable_mask[cpu] & (1 << irq)) - enable_local_vic_irq(irq); - } - vic_irq_enable_mask[cpu] = 0; - spin_unlock(&vic_irq_lock); - - put_cpu_no_resched(); -} - -/* - * CPU halt call-back - */ -static void smp_stop_cpu_function(void *dummy) -{ - VDEBUG(("VOYAGER SMP: CPU%d is STOPPING\n", smp_processor_id())); - cpu_clear(smp_processor_id(), cpu_online_map); - local_irq_disable(); - for (;;) - halt(); -} - -/* execute a thread on a new CPU. The function to be called must be - * previously set up. This is used to schedule a function for - * execution on all CPUs - set up the function then broadcast a - * function_interrupt CPI to come here on each CPU */ -static void smp_call_function_interrupt(void) -{ - irq_enter(); - generic_smp_call_function_interrupt(); - __get_cpu_var(irq_stat).irq_call_count++; - irq_exit(); -} - -static void smp_call_function_single_interrupt(void) -{ - irq_enter(); - generic_smp_call_function_single_interrupt(); - __get_cpu_var(irq_stat).irq_call_count++; - irq_exit(); -} - -/* Sorry about the name. In an APIC based system, the APICs - * themselves are programmed to send a timer interrupt. This is used - * by linux to reschedule the processor. Voyager doesn't have this, - * so we use the system clock to interrupt one processor, which in - * turn, broadcasts a timer CPI to all the others --- we receive that - * CPI here. We don't use this actually for counting so losing - * ticks doesn't matter - * - * FIXME: For those CPUs which actually have a local APIC, we could - * try to use it to trigger this interrupt instead of having to - * broadcast the timer tick. Unfortunately, all my pentium DYADs have - * no local APIC, so I can't do this - * - * This function is currently a placeholder and is unused in the code */ -void smp_apic_timer_interrupt(struct pt_regs *regs) -{ - struct pt_regs *old_regs = set_irq_regs(regs); - wrapper_smp_local_timer_interrupt(); - set_irq_regs(old_regs); -} - -/* All of the QUAD interrupt GATES */ -void smp_qic_timer_interrupt(struct pt_regs *regs) -{ - struct pt_regs *old_regs = set_irq_regs(regs); - ack_QIC_CPI(QIC_TIMER_CPI); - wrapper_smp_local_timer_interrupt(); - set_irq_regs(old_regs); -} - -void smp_qic_invalidate_interrupt(struct pt_regs *regs) -{ - ack_QIC_CPI(QIC_INVALIDATE_CPI); - smp_invalidate_interrupt(); -} - -void smp_qic_reschedule_interrupt(struct pt_regs *regs) -{ - ack_QIC_CPI(QIC_RESCHEDULE_CPI); - smp_reschedule_interrupt(); -} - -void smp_qic_enable_irq_interrupt(struct pt_regs *regs) -{ - ack_QIC_CPI(QIC_ENABLE_IRQ_CPI); - smp_enable_irq_interrupt(); -} - -void smp_qic_call_function_interrupt(struct pt_regs *regs) -{ - ack_QIC_CPI(QIC_CALL_FUNCTION_CPI); - smp_call_function_interrupt(); -} - -void smp_qic_call_function_single_interrupt(struct pt_regs *regs) -{ - ack_QIC_CPI(QIC_CALL_FUNCTION_SINGLE_CPI); - smp_call_function_single_interrupt(); -} - -void smp_vic_cpi_interrupt(struct pt_regs *regs) -{ - struct pt_regs *old_regs = set_irq_regs(regs); - __u8 cpu = smp_processor_id(); - - if (is_cpu_quad()) - ack_QIC_CPI(VIC_CPI_LEVEL0); - else - ack_VIC_CPI(VIC_CPI_LEVEL0); - - if (test_and_clear_bit(VIC_TIMER_CPI, &vic_cpi_mailbox[cpu])) - wrapper_smp_local_timer_interrupt(); - if (test_and_clear_bit(VIC_INVALIDATE_CPI, &vic_cpi_mailbox[cpu])) - smp_invalidate_interrupt(); - if (test_and_clear_bit(VIC_RESCHEDULE_CPI, &vic_cpi_mailbox[cpu])) - smp_reschedule_interrupt(); - if (test_and_clear_bit(VIC_ENABLE_IRQ_CPI, &vic_cpi_mailbox[cpu])) - smp_enable_irq_interrupt(); - if (test_and_clear_bit(VIC_CALL_FUNCTION_CPI, &vic_cpi_mailbox[cpu])) - smp_call_function_interrupt(); - if (test_and_clear_bit(VIC_CALL_FUNCTION_SINGLE_CPI, &vic_cpi_mailbox[cpu])) - smp_call_function_single_interrupt(); - set_irq_regs(old_regs); -} - -static void do_flush_tlb_all(void *info) -{ - unsigned long cpu = smp_processor_id(); - - __flush_tlb_all(); - if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_LAZY) - voyager_leave_mm(cpu); -} - -/* flush the TLB of every active CPU in the system */ -void flush_tlb_all(void) -{ - on_each_cpu(do_flush_tlb_all, 0, 1); -} - -/* send a reschedule CPI to one CPU by physical CPU number*/ -static void voyager_smp_send_reschedule(int cpu) -{ - send_one_CPI(cpu, VIC_RESCHEDULE_CPI); -} - -int hard_smp_processor_id(void) -{ - __u8 i; - __u8 cpumask = inb(VIC_PROC_WHO_AM_I); - if ((cpumask & QUAD_IDENTIFIER) == QUAD_IDENTIFIER) - return cpumask & 0x1F; - - for (i = 0; i < 8; i++) { - if (cpumask & (1 << i)) - return i; - } - printk("** WARNING ** Illegal cpuid returned by VIC: %d", cpumask); - return 0; -} - -int safe_smp_processor_id(void) -{ - return hard_smp_processor_id(); -} - -/* broadcast a halt to all other CPUs */ -static void voyager_smp_send_stop(void) -{ - smp_call_function(smp_stop_cpu_function, NULL, 1); -} - -/* this function is triggered in time.c when a clock tick fires - * we need to re-broadcast the tick to all CPUs */ -void smp_vic_timer_interrupt(void) -{ - send_CPI_allbutself(VIC_TIMER_CPI); - smp_local_timer_interrupt(); -} - -/* local (per CPU) timer interrupt. It does both profiling and - * process statistics/rescheduling. - * - * We do profiling in every local tick, statistics/rescheduling - * happen only every 'profiling multiplier' ticks. The default - * multiplier is 1 and it can be changed by writing the new multiplier - * value into /proc/profile. - */ -void smp_local_timer_interrupt(void) -{ - int cpu = smp_processor_id(); - long weight; - - profile_tick(CPU_PROFILING); - if (--per_cpu(prof_counter, cpu) <= 0) { - /* - * The multiplier may have changed since the last time we got - * to this point as a result of the user writing to - * /proc/profile. In this case we need to adjust the APIC - * timer accordingly. - * - * Interrupts are already masked off at this point. - */ - per_cpu(prof_counter, cpu) = per_cpu(prof_multiplier, cpu); - if (per_cpu(prof_counter, cpu) != - per_cpu(prof_old_multiplier, cpu)) { - /* FIXME: need to update the vic timer tick here */ - per_cpu(prof_old_multiplier, cpu) = - per_cpu(prof_counter, cpu); - } - - update_process_times(user_mode_vm(get_irq_regs())); - } - - if (((1 << cpu) & voyager_extended_vic_processors) == 0) - /* only extended VIC processors participate in - * interrupt distribution */ - return; - - /* - * We take the 'long' return path, and there every subsystem - * grabs the appropriate locks (kernel lock/ irq lock). - * - * we might want to decouple profiling from the 'long path', - * and do the profiling totally in assembly. - * - * Currently this isn't too much of an issue (performance wise), - * we can take more than 100K local irqs per second on a 100 MHz P5. - */ - - if ((++vic_tick[cpu] & 0x7) != 0) - return; - /* get here every 16 ticks (about every 1/6 of a second) */ - - /* Change our priority to give someone else a chance at getting - * the IRQ. The algorithm goes like this: - * - * In the VIC, the dynamically routed interrupt is always - * handled by the lowest priority eligible (i.e. receiving - * interrupts) CPU. If >1 eligible CPUs are equal lowest, the - * lowest processor number gets it. - * - * The priority of a CPU is controlled by a special per-CPU - * VIC priority register which is 3 bits wide 0 being lowest - * and 7 highest priority.. - * - * Therefore we subtract the average number of interrupts from - * the number we've fielded. If this number is negative, we - * lower the activity count and if it is positive, we raise - * it. - * - * I'm afraid this still leads to odd looking interrupt counts: - * the totals are all roughly equal, but the individual ones - * look rather skewed. - * - * FIXME: This algorithm is total crap when mixed with SMP - * affinity code since we now try to even up the interrupt - * counts when an affinity binding is keeping them on a - * particular CPU*/ - weight = (vic_intr_count[cpu] * voyager_extended_cpus - - vic_intr_total) >> 4; - weight += 4; - if (weight > 7) - weight = 7; - if (weight < 0) - weight = 0; - - outb((__u8) weight, VIC_PRIORITY_REGISTER); - -#ifdef VOYAGER_DEBUG - if ((vic_tick[cpu] & 0xFFF) == 0) { - /* print this message roughly every 25 secs */ - printk("VOYAGER SMP: vic_tick[%d] = %lu, weight = %ld\n", - cpu, vic_tick[cpu], weight); - } -#endif -} - -/* setup the profiling timer */ -int setup_profiling_timer(unsigned int multiplier) -{ - int i; - - if ((!multiplier)) - return -EINVAL; - - /* - * Set the new multiplier for each CPU. CPUs don't start using the - * new values until the next timer interrupt in which they do process - * accounting. - */ - for (i = 0; i < nr_cpu_ids; ++i) - per_cpu(prof_multiplier, i) = multiplier; - - return 0; -} - -/* This is a bit of a mess, but forced on us by the genirq changes - * there's no genirq handler that really does what voyager wants - * so hack it up with the simple IRQ handler */ -static void handle_vic_irq(unsigned int irq, struct irq_desc *desc) -{ - before_handle_vic_irq(irq); - handle_simple_irq(irq, desc); - after_handle_vic_irq(irq); -} - -/* The CPIs are handled in the per cpu 8259s, so they must be - * enabled to be received: FIX: enabling the CPIs in the early - * boot sequence interferes with bug checking; enable them later - * on in smp_init */ -#define VIC_SET_GATE(cpi, vector) \ - set_intr_gate((cpi) + VIC_DEFAULT_CPI_BASE, (vector)) -#define QIC_SET_GATE(cpi, vector) \ - set_intr_gate((cpi) + QIC_DEFAULT_CPI_BASE, (vector)) - -void __init voyager_smp_intr_init(void) -{ - int i; - - /* initialize the per cpu irq mask to all disabled */ - for (i = 0; i < nr_cpu_ids; i++) - vic_irq_mask[i] = 0xFFFF; - - VIC_SET_GATE(VIC_CPI_LEVEL0, vic_cpi_interrupt); - - VIC_SET_GATE(VIC_SYS_INT, vic_sys_interrupt); - VIC_SET_GATE(VIC_CMN_INT, vic_cmn_interrupt); - - QIC_SET_GATE(QIC_TIMER_CPI, qic_timer_interrupt); - QIC_SET_GATE(QIC_INVALIDATE_CPI, qic_invalidate_interrupt); - QIC_SET_GATE(QIC_RESCHEDULE_CPI, qic_reschedule_interrupt); - QIC_SET_GATE(QIC_ENABLE_IRQ_CPI, qic_enable_irq_interrupt); - QIC_SET_GATE(QIC_CALL_FUNCTION_CPI, qic_call_function_interrupt); - - /* now put the VIC descriptor into the first 48 IRQs - * - * This is for later: first 16 correspond to PC IRQs; next 16 - * are Primary MC IRQs and final 16 are Secondary MC IRQs */ - for (i = 0; i < 48; i++) - set_irq_chip_and_handler(i, &vic_chip, handle_vic_irq); -} - -/* send a CPI at level cpi to a set of cpus in cpuset (set 1 bit per - * processor to receive CPI */ -static void send_CPI(__u32 cpuset, __u8 cpi) -{ - int cpu; - __u32 quad_cpuset = (cpuset & voyager_quad_processors); - - if (cpi < VIC_START_FAKE_CPI) { - /* fake CPI are only used for booting, so send to the - * extended quads as well---Quads must be VIC booted */ - outb((__u8) (cpuset), VIC_CPI_Registers[cpi]); - return; - } - if (quad_cpuset) - send_QIC_CPI(quad_cpuset, cpi); - cpuset &= ~quad_cpuset; - cpuset &= 0xff; /* only first 8 CPUs vaild for VIC CPI */ - if (cpuset == 0) - return; - for_each_online_cpu(cpu) { - if (cpuset & (1 << cpu)) - set_bit(cpi, &vic_cpi_mailbox[cpu]); - } - if (cpuset) - outb((__u8) cpuset, VIC_CPI_Registers[VIC_CPI_LEVEL0]); -} - -/* Acknowledge receipt of CPI in the QIC, clear in QIC hardware and - * set the cache line to shared by reading it. - * - * DON'T make this inline otherwise the cache line read will be - * optimised away - * */ -static int ack_QIC_CPI(__u8 cpi) -{ - __u8 cpu = hard_smp_processor_id(); - - cpi &= 7; - - outb(1 << cpi, QIC_INTERRUPT_CLEAR1); - return voyager_quad_cpi_addr[cpu]->qic_cpi[cpi].cpi; -} - -static void ack_special_QIC_CPI(__u8 cpi) -{ - switch (cpi) { - case VIC_CMN_INT: - outb(QIC_CMN_INT, QIC_INTERRUPT_CLEAR0); - break; - case VIC_SYS_INT: - outb(QIC_SYS_INT, QIC_INTERRUPT_CLEAR0); - break; - } - /* also clear at the VIC, just in case (nop for non-extended proc) */ - ack_VIC_CPI(cpi); -} - -/* Acknowledge receipt of CPI in the VIC (essentially an EOI) */ -static void ack_VIC_CPI(__u8 cpi) -{ -#ifdef VOYAGER_DEBUG - unsigned long flags; - __u16 isr; - __u8 cpu = smp_processor_id(); - - local_irq_save(flags); - isr = vic_read_isr(); - if ((isr & (1 << (cpi & 7))) == 0) { - printk("VOYAGER SMP: CPU%d lost CPI%d\n", cpu, cpi); - } -#endif - /* send specific EOI; the two system interrupts have - * bit 4 set for a separate vector but behave as the - * corresponding 3 bit intr */ - outb_p(0x60 | (cpi & 7), 0x20); - -#ifdef VOYAGER_DEBUG - if ((vic_read_isr() & (1 << (cpi & 7))) != 0) { - printk("VOYAGER SMP: CPU%d still asserting CPI%d\n", cpu, cpi); - } - local_irq_restore(flags); -#endif -} - -/* cribbed with thanks from irq.c */ -#define __byte(x,y) (((unsigned char *)&(y))[x]) -#define cached_21(cpu) (__byte(0,vic_irq_mask[cpu])) -#define cached_A1(cpu) (__byte(1,vic_irq_mask[cpu])) - -static unsigned int startup_vic_irq(unsigned int irq) -{ - unmask_vic_irq(irq); - - return 0; -} - -/* The enable and disable routines. This is where we run into - * conflicting architectural philosophy. Fundamentally, the voyager - * architecture does not expect to have to disable interrupts globally - * (the IRQ controllers belong to each CPU). The processor masquerade - * which is used to start the system shouldn't be used in a running OS - * since it will cause great confusion if two separate CPUs drive to - * the same IRQ controller (I know, I've tried it). - * - * The solution is a variant on the NCR lazy SPL design: - * - * 1) To disable an interrupt, do nothing (other than set the - * IRQ_DISABLED flag). This dares the interrupt actually to arrive. - * - * 2) If the interrupt dares to come in, raise the local mask against - * it (this will result in all the CPU masks being raised - * eventually). - * - * 3) To enable the interrupt, lower the mask on the local CPU and - * broadcast an Interrupt enable CPI which causes all other CPUs to - * adjust their masks accordingly. */ - -static void unmask_vic_irq(unsigned int irq) -{ - /* linux doesn't to processor-irq affinity, so enable on - * all CPUs we know about */ - int cpu = smp_processor_id(), real_cpu; - __u16 mask = (1 << irq); - __u32 processorList = 0; - unsigned long flags; - - VDEBUG(("VOYAGER: unmask_vic_irq(%d) CPU%d affinity 0x%lx\n", - irq, cpu, cpu_irq_affinity[cpu])); - spin_lock_irqsave(&vic_irq_lock, flags); - for_each_online_cpu(real_cpu) { - if (!(voyager_extended_vic_processors & (1 << real_cpu))) - continue; - if (!(cpu_irq_affinity[real_cpu] & mask)) { - /* irq has no affinity for this CPU, ignore */ - continue; - } - if (real_cpu == cpu) { - enable_local_vic_irq(irq); - } else if (vic_irq_mask[real_cpu] & mask) { - vic_irq_enable_mask[real_cpu] |= mask; - processorList |= (1 << real_cpu); - } - } - spin_unlock_irqrestore(&vic_irq_lock, flags); - if (processorList) - send_CPI(processorList, VIC_ENABLE_IRQ_CPI); -} - -static void mask_vic_irq(unsigned int irq) -{ - /* lazy disable, do nothing */ -} - -static void enable_local_vic_irq(unsigned int irq) -{ - __u8 cpu = smp_processor_id(); - __u16 mask = ~(1 << irq); - __u16 old_mask = vic_irq_mask[cpu]; - - vic_irq_mask[cpu] &= mask; - if (vic_irq_mask[cpu] == old_mask) - return; - - VDEBUG(("VOYAGER DEBUG: Enabling irq %d in hardware on CPU %d\n", - irq, cpu)); - - if (irq & 8) { - outb_p(cached_A1(cpu), 0xA1); - (void)inb_p(0xA1); - } else { - outb_p(cached_21(cpu), 0x21); - (void)inb_p(0x21); - } -} - -static void disable_local_vic_irq(unsigned int irq) -{ - __u8 cpu = smp_processor_id(); - __u16 mask = (1 << irq); - __u16 old_mask = vic_irq_mask[cpu]; - - if (irq == 7) - return; - - vic_irq_mask[cpu] |= mask; - if (old_mask == vic_irq_mask[cpu]) - return; - - VDEBUG(("VOYAGER DEBUG: Disabling irq %d in hardware on CPU %d\n", - irq, cpu)); - - if (irq & 8) { - outb_p(cached_A1(cpu), 0xA1); - (void)inb_p(0xA1); - } else { - outb_p(cached_21(cpu), 0x21); - (void)inb_p(0x21); - } -} - -/* The VIC is level triggered, so the ack can only be issued after the - * interrupt completes. However, we do Voyager lazy interrupt - * handling here: It is an extremely expensive operation to mask an - * interrupt in the vic, so we merely set a flag (IRQ_DISABLED). If - * this interrupt actually comes in, then we mask and ack here to push - * the interrupt off to another CPU */ -static void before_handle_vic_irq(unsigned int irq) -{ - irq_desc_t *desc = irq_to_desc(irq); - __u8 cpu = smp_processor_id(); - - _raw_spin_lock(&vic_irq_lock); - vic_intr_total++; - vic_intr_count[cpu]++; - - if (!(cpu_irq_affinity[cpu] & (1 << irq))) { - /* The irq is not in our affinity mask, push it off - * onto another CPU */ - VDEBUG(("VOYAGER DEBUG: affinity triggered disable of irq %d " - "on cpu %d\n", irq, cpu)); - disable_local_vic_irq(irq); - /* set IRQ_INPROGRESS to prevent the handler in irq.c from - * actually calling the interrupt routine */ - desc->status |= IRQ_REPLAY | IRQ_INPROGRESS; - } else if (desc->status & IRQ_DISABLED) { - /* Damn, the interrupt actually arrived, do the lazy - * disable thing. The interrupt routine in irq.c will - * not handle a IRQ_DISABLED interrupt, so nothing more - * need be done here */ - VDEBUG(("VOYAGER DEBUG: lazy disable of irq %d on CPU %d\n", - irq, cpu)); - disable_local_vic_irq(irq); - desc->status |= IRQ_REPLAY; - } else { - desc->status &= ~IRQ_REPLAY; - } - - _raw_spin_unlock(&vic_irq_lock); -} - -/* Finish the VIC interrupt: basically mask */ -static void after_handle_vic_irq(unsigned int irq) -{ - irq_desc_t *desc = irq_to_desc(irq); - - _raw_spin_lock(&vic_irq_lock); - { - unsigned int status = desc->status & ~IRQ_INPROGRESS; -#ifdef VOYAGER_DEBUG - __u16 isr; -#endif - - desc->status = status; - if ((status & IRQ_DISABLED)) - disable_local_vic_irq(irq); -#ifdef VOYAGER_DEBUG - /* DEBUG: before we ack, check what's in progress */ - isr = vic_read_isr(); - if ((isr & (1 << irq) && !(status & IRQ_REPLAY)) == 0) { - int i; - __u8 cpu = smp_processor_id(); - __u8 real_cpu; - int mask; /* Um... initialize me??? --RR */ - - printk("VOYAGER SMP: CPU%d lost interrupt %d\n", - cpu, irq); - for_each_possible_cpu(real_cpu, mask) { - - outb(VIC_CPU_MASQUERADE_ENABLE | real_cpu, - VIC_PROCESSOR_ID); - isr = vic_read_isr(); - if (isr & (1 << irq)) { - printk - ("VOYAGER SMP: CPU%d ack irq %d\n", - real_cpu, irq); - ack_vic_irq(irq); - } - outb(cpu, VIC_PROCESSOR_ID); - } - } -#endif /* VOYAGER_DEBUG */ - /* as soon as we ack, the interrupt is eligible for - * receipt by another CPU so everything must be in - * order here */ - ack_vic_irq(irq); - if (status & IRQ_REPLAY) { - /* replay is set if we disable the interrupt - * in the before_handle_vic_irq() routine, so - * clear the in progress bit here to allow the - * next CPU to handle this correctly */ - desc->status &= ~(IRQ_REPLAY | IRQ_INPROGRESS); - } -#ifdef VOYAGER_DEBUG - isr = vic_read_isr(); - if ((isr & (1 << irq)) != 0) - printk("VOYAGER SMP: after_handle_vic_irq() after " - "ack irq=%d, isr=0x%x\n", irq, isr); -#endif /* VOYAGER_DEBUG */ - } - _raw_spin_unlock(&vic_irq_lock); - - /* All code after this point is out of the main path - the IRQ - * may be intercepted by another CPU if reasserted */ -} - -/* Linux processor - interrupt affinity manipulations. - * - * For each processor, we maintain a 32 bit irq affinity mask. - * Initially it is set to all 1's so every processor accepts every - * interrupt. In this call, we change the processor's affinity mask: - * - * Change from enable to disable: - * - * If the interrupt ever comes in to the processor, we will disable it - * and ack it to push it off to another CPU, so just accept the mask here. - * - * Change from disable to enable: - * - * change the mask and then do an interrupt enable CPI to re-enable on - * the selected processors */ - -void set_vic_irq_affinity(unsigned int irq, const struct cpumask *mask) -{ - /* Only extended processors handle interrupts */ - unsigned long real_mask; - unsigned long irq_mask = 1 << irq; - int cpu; - - real_mask = cpus_addr(*mask)[0] & voyager_extended_vic_processors; - - if (cpus_addr(*mask)[0] == 0) - /* can't have no CPUs to accept the interrupt -- extremely - * bad things will happen */ - return; - - if (irq == 0) - /* can't change the affinity of the timer IRQ. This - * is due to the constraint in the voyager - * architecture that the CPI also comes in on and IRQ - * line and we have chosen IRQ0 for this. If you - * raise the mask on this interrupt, the processor - * will no-longer be able to accept VIC CPIs */ - return; - - if (irq >= 32) - /* You can only have 32 interrupts in a voyager system - * (and 32 only if you have a secondary microchannel - * bus) */ - return; - - for_each_online_cpu(cpu) { - unsigned long cpu_mask = 1 << cpu; - - if (cpu_mask & real_mask) { - /* enable the interrupt for this cpu */ - cpu_irq_affinity[cpu] |= irq_mask; - } else { - /* disable the interrupt for this cpu */ - cpu_irq_affinity[cpu] &= ~irq_mask; - } - } - /* this is magic, we now have the correct affinity maps, so - * enable the interrupt. This will send an enable CPI to - * those CPUs who need to enable it in their local masks, - * causing them to correct for the new affinity . If the - * interrupt is currently globally disabled, it will simply be - * disabled again as it comes in (voyager lazy disable). If - * the affinity map is tightened to disable the interrupt on a - * cpu, it will be pushed off when it comes in */ - unmask_vic_irq(irq); -} - -static void ack_vic_irq(unsigned int irq) -{ - if (irq & 8) { - outb(0x62, 0x20); /* Specific EOI to cascade */ - outb(0x60 | (irq & 7), 0xA0); - } else { - outb(0x60 | (irq & 7), 0x20); - } -} - -/* enable the CPIs. In the VIC, the CPIs are delivered by the 8259 - * but are not vectored by it. This means that the 8259 mask must be - * lowered to receive them */ -static __init void vic_enable_cpi(void) -{ - __u8 cpu = smp_processor_id(); - - /* just take a copy of the current mask (nop for boot cpu) */ - vic_irq_mask[cpu] = vic_irq_mask[boot_cpu_id]; - - enable_local_vic_irq(VIC_CPI_LEVEL0); - enable_local_vic_irq(VIC_CPI_LEVEL1); - /* for sys int and cmn int */ - enable_local_vic_irq(7); - - if (is_cpu_quad()) { - outb(QIC_DEFAULT_MASK0, QIC_MASK_REGISTER0); - outb(QIC_CPI_ENABLE, QIC_MASK_REGISTER1); - VDEBUG(("VOYAGER SMP: QIC ENABLE CPI: CPU%d: MASK 0x%x\n", - cpu, QIC_CPI_ENABLE)); - } - - VDEBUG(("VOYAGER SMP: ENABLE CPI: CPU%d: MASK 0x%x\n", - cpu, vic_irq_mask[cpu])); -} - -void voyager_smp_dump() -{ - int old_cpu = smp_processor_id(), cpu; - - /* dump the interrupt masks of each processor */ - for_each_online_cpu(cpu) { - __u16 imr, isr, irr; - unsigned long flags; - - local_irq_save(flags); - outb(VIC_CPU_MASQUERADE_ENABLE | cpu, VIC_PROCESSOR_ID); - imr = (inb(0xa1) << 8) | inb(0x21); - outb(0x0a, 0xa0); - irr = inb(0xa0) << 8; - outb(0x0a, 0x20); - irr |= inb(0x20); - outb(0x0b, 0xa0); - isr = inb(0xa0) << 8; - outb(0x0b, 0x20); - isr |= inb(0x20); - outb(old_cpu, VIC_PROCESSOR_ID); - local_irq_restore(flags); - printk("\tCPU%d: mask=0x%x, IMR=0x%x, IRR=0x%x, ISR=0x%x\n", - cpu, vic_irq_mask[cpu], imr, irr, isr); -#if 0 - /* These lines are put in to try to unstick an un ack'd irq */ - if (isr != 0) { - int irq; - for (irq = 0; irq < 16; irq++) { - if (isr & (1 << irq)) { - printk("\tCPU%d: ack irq %d\n", - cpu, irq); - local_irq_save(flags); - outb(VIC_CPU_MASQUERADE_ENABLE | cpu, - VIC_PROCESSOR_ID); - ack_vic_irq(irq); - outb(old_cpu, VIC_PROCESSOR_ID); - local_irq_restore(flags); - } - } - } -#endif - } -} - -void smp_voyager_power_off(void *dummy) -{ - if (smp_processor_id() == boot_cpu_id) - voyager_power_off(); - else - smp_stop_cpu_function(NULL); -} - -static void __init voyager_smp_prepare_cpus(unsigned int max_cpus) -{ - /* FIXME: ignore max_cpus for now */ - smp_boot_cpus(); -} - -static void __cpuinit voyager_smp_prepare_boot_cpu(void) -{ - int cpu = smp_processor_id(); - switch_to_new_gdt(cpu); - - cpu_set(cpu, cpu_online_map); - cpu_set(cpu, cpu_callout_map); - cpu_set(cpu, cpu_possible_map); - cpu_set(cpu, cpu_present_map); - -} - -static int __cpuinit voyager_cpu_up(unsigned int cpu) -{ - /* This only works at boot for x86. See "rewrite" above. */ - if (cpu_isset(cpu, smp_commenced_mask)) - return -ENOSYS; - - /* In case one didn't come up */ - if (!cpu_isset(cpu, cpu_callin_map)) - return -EIO; - /* Unleash the CPU! */ - cpu_set(cpu, smp_commenced_mask); - while (!cpu_online(cpu)) - mb(); - return 0; -} - -static void __init voyager_smp_cpus_done(unsigned int max_cpus) -{ - zap_low_mappings(); -} - -void __init smp_setup_processor_id(void) -{ - current_thread_info()->cpu = hard_smp_processor_id(); -} - -static void voyager_send_call_func(const struct cpumask *callmask) -{ - __u32 mask = cpus_addr(*callmask)[0] & ~(1 << smp_processor_id()); - send_CPI(mask, VIC_CALL_FUNCTION_CPI); -} - -static void voyager_send_call_func_single(int cpu) -{ - send_CPI(1 << cpu, VIC_CALL_FUNCTION_SINGLE_CPI); -} - -struct smp_ops smp_ops = { - .smp_prepare_boot_cpu = voyager_smp_prepare_boot_cpu, - .smp_prepare_cpus = voyager_smp_prepare_cpus, - .cpu_up = voyager_cpu_up, - .smp_cpus_done = voyager_smp_cpus_done, - - .smp_send_stop = voyager_smp_send_stop, - .smp_send_reschedule = voyager_smp_send_reschedule, - - .send_call_func_ipi = voyager_send_call_func, - .send_call_func_single_ipi = voyager_send_call_func_single, -}; diff --git a/arch/x86/mach-voyager/voyager_thread.c b/arch/x86/mach-voyager/voyager_thread.c deleted file mode 100644 index 15464a20fb38..000000000000 --- a/arch/x86/mach-voyager/voyager_thread.c +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8 -*- */ - -/* Copyright (C) 2001 - * - * Author: J.E.J.Bottomley@HansenPartnership.com - * - * This module provides the machine status monitor thread for the - * voyager architecture. This allows us to monitor the machine - * environment (temp, voltage, fan function) and the front panel and - * internal UPS. If a fault is detected, this thread takes corrective - * action (usually just informing init) - * */ - -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/kernel_stat.h> -#include <linux/delay.h> -#include <linux/mc146818rtc.h> -#include <linux/init.h> -#include <linux/bootmem.h> -#include <linux/kmod.h> -#include <linux/completion.h> -#include <linux/sched.h> -#include <linux/kthread.h> -#include <asm/desc.h> -#include <asm/voyager.h> -#include <asm/vic.h> -#include <asm/mtrr.h> -#include <asm/msr.h> - -struct task_struct *voyager_thread; -static __u8 set_timeout; - -static int execute(const char *string) -{ - int ret; - - char *envp[] = { - "HOME=/", - "TERM=linux", - "PATH=/sbin:/usr/sbin:/bin:/usr/bin", - NULL, - }; - char *argv[] = { - "/bin/bash", - "-c", - (char *)string, - NULL, - }; - - if ((ret = - call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC)) != 0) { - printk(KERN_ERR "Voyager failed to run \"%s\": %i\n", string, - ret); - } - return ret; -} - -static void check_from_kernel(void) -{ - if (voyager_status.switch_off) { - - /* FIXME: This should be configurable via proc */ - execute("umask 600; echo 0 > /etc/initrunlvl; kill -HUP 1"); - } else if (voyager_status.power_fail) { - VDEBUG(("Voyager daemon detected AC power failure\n")); - - /* FIXME: This should be configureable via proc */ - execute("umask 600; echo F > /etc/powerstatus; kill -PWR 1"); - set_timeout = 1; - } -} - -static void check_continuing_condition(void) -{ - if (voyager_status.power_fail) { - __u8 data; - voyager_cat_psi(VOYAGER_PSI_SUBREAD, - VOYAGER_PSI_AC_FAIL_REG, &data); - if ((data & 0x1f) == 0) { - /* all power restored */ - printk(KERN_NOTICE - "VOYAGER AC power restored, cancelling shutdown\n"); - /* FIXME: should be user configureable */ - execute - ("umask 600; echo O > /etc/powerstatus; kill -PWR 1"); - set_timeout = 0; - } - } -} - -static int thread(void *unused) -{ - printk(KERN_NOTICE "Voyager starting monitor thread\n"); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(set_timeout ? HZ : MAX_SCHEDULE_TIMEOUT); - - VDEBUG(("Voyager Daemon awoken\n")); - if (voyager_status.request_from_kernel == 0) { - /* probably awoken from timeout */ - check_continuing_condition(); - } else { - check_from_kernel(); - voyager_status.request_from_kernel = 0; - } - } -} - -static int __init voyager_thread_start(void) -{ - voyager_thread = kthread_run(thread, NULL, "kvoyagerd"); - if (IS_ERR(voyager_thread)) { - printk(KERN_ERR - "Voyager: Failed to create system monitor thread.\n"); - return PTR_ERR(voyager_thread); - } - return 0; -} - -static void __exit voyager_thread_stop(void) -{ - kthread_stop(voyager_thread); -} - -module_init(voyager_thread_start); -module_exit(voyager_thread_stop); diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig index 87b9ab166423..b83e119fbeb0 100644 --- a/arch/x86/xen/Kconfig +++ b/arch/x86/xen/Kconfig @@ -6,7 +6,7 @@ config XEN bool "Xen guest support" select PARAVIRT select PARAVIRT_CLOCK - depends on X86_64 || (X86_32 && X86_PAE && !(X86_VISWS || X86_VOYAGER)) + depends on X86_64 || (X86_32 && X86_PAE && !X86_VISWS) depends on X86_CMPXCHG && X86_TSC help This is the Linux Xen port. Enabling this will allow the diff --git a/drivers/lguest/Kconfig b/drivers/lguest/Kconfig index 76f2b36881c3..a3d3cbab359a 100644 --- a/drivers/lguest/Kconfig +++ b/drivers/lguest/Kconfig @@ -1,6 +1,6 @@ config LGUEST tristate "Linux hypervisor example code" - depends on X86_32 && EXPERIMENTAL && !X86_PAE && FUTEX && !X86_VOYAGER + depends on X86_32 && EXPERIMENTAL && !X86_PAE && FUTEX select HVC_DRIVER ---help--- This is a very simple module which allows you to run -- cgit v1.2.3 From 0d3a9cf5ab041c15691fd03dab3af0841af63606 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com> Date: Sun, 22 Feb 2009 14:58:56 -0800 Subject: acpi: add some missing section markers early_acpi_os_unmap_memory() is an __init function, and acpi_os_unmap_memory() is allowed to access an __init function until acpi_gbl_permanent_mmap is set up. Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com> Cc: Len Brown <len.brown@intel.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- drivers/acpi/osl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index d1dd5160daa9..2b6c59028254 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -272,7 +272,7 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) } EXPORT_SYMBOL_GPL(acpi_os_map_memory); -void acpi_os_unmap_memory(void __iomem * virt, acpi_size size) +void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) { if (acpi_gbl_permanent_mmap) iounmap(virt); @@ -281,7 +281,7 @@ void acpi_os_unmap_memory(void __iomem * virt, acpi_size size) } EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); -void early_acpi_os_unmap_memory(void __iomem * virt, acpi_size size) +void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) { if (!acpi_gbl_permanent_mmap) __acpi_unmap_table(virt, size); -- cgit v1.2.3 From 8b0e378a20e48c691d374f39d8b0596e63598cfc Mon Sep 17 00:00:00 2001 From: Eric Anholt <eric@anholt.net> Date: Thu, 19 Feb 2009 14:40:50 -0800 Subject: drm/i915: Cut two args to set_to_gpu_domain that confused this tricky path. While not strictly required, it helped while thinking about the following change. This change should be invariant. Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/i915_gem.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ac534c9a2f81..02ef50d512d6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -34,10 +34,6 @@ #define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) -static void -i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, - uint32_t read_domains, - uint32_t write_domain); static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj); @@ -2021,30 +2017,28 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) * drm_agp_chipset_flush */ static void -i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, - uint32_t read_domains, - uint32_t write_domain) +i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; uint32_t invalidate_domains = 0; uint32_t flush_domains = 0; - BUG_ON(read_domains & I915_GEM_DOMAIN_CPU); - BUG_ON(write_domain == I915_GEM_DOMAIN_CPU); + BUG_ON(obj->pending_read_domains & I915_GEM_DOMAIN_CPU); + BUG_ON(obj->pending_write_domain == I915_GEM_DOMAIN_CPU); #if WATCH_BUF DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n", __func__, obj, - obj->read_domains, read_domains, - obj->write_domain, write_domain); + obj->read_domains, obj->pending_read_domains, + obj->write_domain, obj->pending_write_domain); #endif /* * If the object isn't moving to a new write domain, * let the object stay in multiple read domains */ - if (write_domain == 0) - read_domains |= obj->read_domains; + if (obj->pending_write_domain == 0) + obj->pending_read_domains |= obj->read_domains; else obj_priv->dirty = 1; @@ -2054,15 +2048,17 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, * any read domains which differ from the old * write domain */ - if (obj->write_domain && obj->write_domain != read_domains) { + if (obj->write_domain && + obj->write_domain != obj->pending_read_domains) { flush_domains |= obj->write_domain; - invalidate_domains |= read_domains & ~obj->write_domain; + invalidate_domains |= + obj->pending_read_domains & ~obj->write_domain; } /* * Invalidate any read caches which may have * stale data. That is, any new read domains. */ - invalidate_domains |= read_domains & ~obj->read_domains; + invalidate_domains |= obj->pending_read_domains & ~obj->read_domains; if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) { #if WATCH_BUF DRM_INFO("%s: CPU domain flush %08x invalidate %08x\n", @@ -2071,9 +2067,9 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, i915_gem_clflush_object(obj); } - if ((write_domain | flush_domains) != 0) - obj->write_domain = write_domain; - obj->read_domains = read_domains; + if ((obj->pending_write_domain | flush_domains) != 0) + obj->write_domain = obj->pending_write_domain; + obj->read_domains = obj->pending_read_domains; dev->invalidate_domains |= invalidate_domains; dev->flush_domains |= flush_domains; @@ -2583,9 +2579,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_gem_object *obj = object_list[i]; /* Compute new gpu domains and update invalidate/flush */ - i915_gem_object_set_to_gpu_domain(obj, - obj->pending_read_domains, - obj->pending_write_domain); + i915_gem_object_set_to_gpu_domain(obj); } i915_verify_inactive(dev, __FILE__, __LINE__); -- cgit v1.2.3 From efbeed96f7e20783b22d9529ef536b61f7ea8637 Mon Sep 17 00:00:00 2001 From: Eric Anholt <eric@anholt.net> Date: Thu, 19 Feb 2009 14:54:51 -0800 Subject: drm/i915: Don't let a device flush to prepare buffers clear new write_domains. The problem was that object_set_to_gpu_domain would set the new write_domains that are getting set by this batchbuffer, then the accumulated flushes required for all the objects in preparation for this batchbuffer were posted, and the brand new write domain would get cleared by the flush being posted. Instead, hang on to the new (or old if we're not changing it) value and set it after the flush is queued. Results from this noticably included conformance test failures from reads shortly after writes (where the new write domain had been lost and thus not flushed and waited on), but is a suspected cause of hangs in some apps when a write domain is lost on a buffer that gets reused for instruction or commmand state. Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/i915_gem.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 02ef50d512d6..0f50574076b1 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2067,8 +2067,14 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) i915_gem_clflush_object(obj); } - if ((obj->pending_write_domain | flush_domains) != 0) - obj->write_domain = obj->pending_write_domain; + /* The actual obj->write_domain will be updated with + * pending_write_domain after we emit the accumulated flush for all + * of our domain changes in execbuffers (which clears objects' + * write_domains). So if we have a current write domain that we + * aren't changing, set pending_write_domain to that. + */ + if (flush_domains == 0 && obj->pending_write_domain == 0) + obj->pending_write_domain = obj->write_domain; obj->read_domains = obj->pending_read_domains; dev->invalidate_domains |= invalidate_domains; @@ -2598,6 +2604,12 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, (void)i915_add_request(dev, dev->flush_domains); } + for (i = 0; i < args->buffer_count; i++) { + struct drm_gem_object *obj = object_list[i]; + + obj->write_domain = obj->pending_write_domain; + } + i915_verify_inactive(dev, __FILE__, __LINE__); #if WATCH_COHERENCY -- cgit v1.2.3 From 5669fcacc58bf3a7386057addffd280d75380858 Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Tue, 17 Feb 2009 15:13:31 -0800 Subject: drm/i915: suspend/resume GEM when KMS is active In the KMS case, we need to suspend/resume GEM as well. So on suspend, make sure we idle GEM and stop any new rendering from coming in, and on resume, re-init the framebuffer and clear the suspended flag. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/i915_drv.c | 23 ++++++++++++++++++++++- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a31cbdbc3c54..0692622ee2b3 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -27,6 +27,7 @@ * */ +#include <linux/device.h> #include "drmP.h" #include "drm.h" #include "i915_drm.h" @@ -66,6 +67,12 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) i915_save_state(dev); + /* If KMS is active, we do the leavevt stuff here */ + if (drm_core_check_feature(dev, DRIVER_MODESET) && i915_gem_idle(dev)) { + dev_err(&dev->pdev->dev, "GEM idle failed, aborting suspend\n"); + return -EBUSY; + } + intel_opregion_free(dev); if (state.event == PM_EVENT_SUSPEND) { @@ -79,6 +86,9 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) static int i915_resume(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; + int ret = 0; + pci_set_power_state(dev->pdev, PCI_D0); pci_restore_state(dev->pdev); if (pci_enable_device(dev->pdev)) @@ -89,7 +99,18 @@ static int i915_resume(struct drm_device *dev) intel_opregion_init(dev); - return 0; + /* KMS EnterVT equivalent */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + mutex_lock(&dev->struct_mutex); + dev_priv->mm.suspended = 0; + + ret = i915_gem_init_ringbuffer(dev); + if (ret != 0) + ret = -1; + mutex_unlock(&dev->struct_mutex); + } + + return ret; } static struct vm_operations_struct i915_gem_vm_ops = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 135a08f615cd..17fa40858d26 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -618,6 +618,7 @@ int i915_gem_init_ringbuffer(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); int i915_gem_do_init(struct drm_device *dev, unsigned long start, unsigned long end); +int i915_gem_idle(struct drm_device *dev); int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0f50574076b1..58c789da95a3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2973,7 +2973,7 @@ i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) return 0; } -static int +int i915_gem_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; -- cgit v1.2.3 From f21289b355cee8738d80c2ae5cbd272c3f7b5689 Mon Sep 17 00:00:00 2001 From: Eric Anholt <eric@anholt.net> Date: Wed, 18 Feb 2009 09:44:56 -0800 Subject: drm/i915: Retire requests from i915_gem_busy_ioctl. This ensures that the user gets the latest information from the hardware on whether the buffer is busy, potentially reducing the working set of objects that the user chooses. Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/i915_gem.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 58c789da95a3..8b50d4820389 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2872,6 +2872,13 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, return -EBADF; } + /* Update the active list for the hardware's current position. + * Otherwise this only updates on a delayed timer or when irqs are + * actually unmasked, and our working set ends up being larger than + * required. + */ + i915_gem_retire_requests(dev); + obj_priv = obj->driver_private; /* Don't count being on the flushing list against the object being * done. Otherwise, a buffer left on the flushing list but not getting -- cgit v1.2.3 From bab2d1f6531657e37dc84f26184f3f64e1e73ecd Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Fri, 20 Feb 2009 17:52:20 +0000 Subject: drm/i915: Fix regression in 95ca9d The object is dereferenced before the NULL check. Oops. Fixes http://bugs.freedesktop.org/show_bug.cgi?id=20235 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/i915_gem.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8b50d4820389..25b337438ca7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3143,16 +3143,20 @@ static void i915_gem_cleanup_hws(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_gem_object *obj = dev_priv->hws_obj; - struct drm_i915_gem_object *obj_priv = obj->driver_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; if (dev_priv->hws_obj == NULL) return; + obj = dev_priv->hws_obj; + obj_priv = obj->driver_private; + kunmap(obj_priv->page_list[0]); i915_gem_object_unpin(obj); drm_gem_object_unreference(obj); dev_priv->hws_obj = NULL; + memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); dev_priv->hw_status_page = NULL; -- cgit v1.2.3 From 6fb88588555a18792a27f483887fe1f2af5f9c9b Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Mon, 23 Feb 2009 10:08:21 +1000 Subject: drm/i915: fix WC mapping in non-GEM i915 code. [airlied - taken from mailing list posting] Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/i915_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 81f1cff56fd5..2d797ffe8137 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -202,7 +202,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) dev_priv->ring.map.flags = 0; dev_priv->ring.map.mtrr = 0; - drm_core_ioremap(&dev_priv->ring.map, dev); + drm_core_ioremap_wc(&dev_priv->ring.map, dev); if (dev_priv->ring.map.handle == NULL) { i915_dma_cleanup(dev); -- cgit v1.2.3 From 5004417d840e6dcb0052061fd04569b9c9f037a8 Mon Sep 17 00:00:00 2001 From: Pierre Willenbrock <pierre@pirsoft.de> Date: Mon, 23 Feb 2009 10:12:15 +1000 Subject: drm/i915: Add missing mutex_lock(&dev->struct_mutex) there might be a nicer way to fix this but this is the simplest for now. Signed-off-by: Pierre Willenbrock <pierre@pirsoft.de> Signed-off-by: Dave Airlie <airlied@redhat.com> --- drivers/gpu/drm/i915/intel_display.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4d2baf7b00be..65b635ce28c8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1008,6 +1008,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, temp = CURSOR_MODE_DISABLE; addr = 0; bo = NULL; + mutex_lock(&dev->struct_mutex); goto finish; } -- cgit v1.2.3 From 6c0594a306790ab03db345086c0c6c922a900bf6 Mon Sep 17 00:00:00 2001 From: Karsten Wiese <fzu@wemgehoertderstaat.de> Date: Mon, 23 Feb 2009 15:07:57 +0100 Subject: Fix an oops in i915_gem_retire_requests() dev_priv->hw_status_page can be NULL, if i915_gem_retire_requests() is called from i915_gem_busy_ioctl(). Signed-off-by Karsten Wiese <fzu@wemgehoertderstaat.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/gpu/drm/i915/i915_gem.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 25b337438ca7..28b726d07a0c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1051,6 +1051,9 @@ i915_gem_retire_requests(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; uint32_t seqno; + if (!dev_priv->hw_status_page) + return; + seqno = i915_get_gem_seqno(dev); while (!list_empty(&dev_priv->mm.request_list)) { -- cgit v1.2.3 From 226485e9a91ee89c941d8cb7714f85644a8071d0 Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Mon, 23 Feb 2009 15:41:09 -0800 Subject: i915: suspend/resume interrupt state In the KMS case, enter/leavevt won't fix up the interrupt handler for us, so we need to do it at suspend/resume time. Make sure we don't fail the resume if the chip is hung either. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- drivers/gpu/drm/i915/i915_drv.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 0692622ee2b3..b293ef0bae71 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -68,9 +68,11 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) i915_save_state(dev); /* If KMS is active, we do the leavevt stuff here */ - if (drm_core_check_feature(dev, DRIVER_MODESET) && i915_gem_idle(dev)) { - dev_err(&dev->pdev->dev, "GEM idle failed, aborting suspend\n"); - return -EBUSY; + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + if (i915_gem_idle(dev)) + dev_err(&dev->pdev->dev, + "GEM idle failed, resume may fail\n"); + drm_irq_uninstall(dev); } intel_opregion_free(dev); @@ -108,6 +110,8 @@ static int i915_resume(struct drm_device *dev) if (ret != 0) ret = -1; mutex_unlock(&dev->struct_mutex); + + drm_irq_install(dev); } return ret; -- cgit v1.2.3 From c81c8b68b46752721b0c1addfabb828da27e1489 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@suse.de> Date: Thu, 6 Mar 2008 21:30:23 -0800 Subject: DVB: add firesat driver Original code written by Christian Dolzer <c.dolzer@digital-everywhere.com> Cleaned up by Greg. Major cleanup and reorg by Manu Abraham <manu@linuxtv.org> Additions also by Ben Backx <ben@bbackx.com> Cc: Christian Dolzer <c.dolzer@digital-everywhere.com> Cc: Andreas Monitzer <andy@monitzer.com> Cc: Manu Abraham <manu@linuxtv.org> Cc: Fabio De Lorenzo <delorenzo.fabio@gmail.com> Cc: Robert Berger <robert.berger@reliableembeddedsystems.com> Signed-off-by: Ben Backx <ben@bbackx.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Added missing dependency to dvb/firesat/Kconfig, Reported-by: Randy Dunlap <randy.dunlap@oracle.com> Tweaked dvb/Makefile. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/Kconfig | 2 + drivers/media/dvb/Makefile | 2 + drivers/media/dvb/firesat/Kconfig | 11 + drivers/media/dvb/firesat/Makefile | 12 + drivers/media/dvb/firesat/avc_api.c | 848 +++++++++++++++++++++++++++++++ drivers/media/dvb/firesat/avc_api.h | 381 ++++++++++++++ drivers/media/dvb/firesat/cmp.c | 230 +++++++++ drivers/media/dvb/firesat/cmp.h | 9 + drivers/media/dvb/firesat/firesat-ci.c | 95 ++++ drivers/media/dvb/firesat/firesat-ci.h | 9 + drivers/media/dvb/firesat/firesat-rc.c | 84 +++ drivers/media/dvb/firesat/firesat-rc.h | 9 + drivers/media/dvb/firesat/firesat.h | 85 ++++ drivers/media/dvb/firesat/firesat_1394.c | 468 +++++++++++++++++ drivers/media/dvb/firesat/firesat_dvb.c | 350 +++++++++++++ drivers/media/dvb/firesat/firesat_fe.c | 263 ++++++++++ 16 files changed, 2858 insertions(+) create mode 100644 drivers/media/dvb/firesat/Kconfig create mode 100644 drivers/media/dvb/firesat/Makefile create mode 100644 drivers/media/dvb/firesat/avc_api.c create mode 100644 drivers/media/dvb/firesat/avc_api.h create mode 100644 drivers/media/dvb/firesat/cmp.c create mode 100644 drivers/media/dvb/firesat/cmp.h create mode 100644 drivers/media/dvb/firesat/firesat-ci.c create mode 100644 drivers/media/dvb/firesat/firesat-ci.h create mode 100644 drivers/media/dvb/firesat/firesat-rc.c create mode 100644 drivers/media/dvb/firesat/firesat-rc.h create mode 100644 drivers/media/dvb/firesat/firesat.h create mode 100644 drivers/media/dvb/firesat/firesat_1394.c create mode 100644 drivers/media/dvb/firesat/firesat_dvb.c create mode 100644 drivers/media/dvb/firesat/firesat_fe.c (limited to 'drivers') diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 40ebde53b3ce..8a2d5f9713de 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -51,6 +51,8 @@ comment "Supported SDMC DM1105 Adapters" depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/dm1105/Kconfig" +source "drivers/media/dvb/firesat/Kconfig" + comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb/frontends/Kconfig" diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index f91e9eb15e52..41710554012f 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -3,3 +3,5 @@ # obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ + +obj-$(CONFIG_DVB_FIRESAT) += firesat/ diff --git a/drivers/media/dvb/firesat/Kconfig b/drivers/media/dvb/firesat/Kconfig new file mode 100644 index 000000000000..93f8de5ec3c8 --- /dev/null +++ b/drivers/media/dvb/firesat/Kconfig @@ -0,0 +1,11 @@ +config DVB_FIRESAT + tristate "FireSAT devices" + depends on DVB_CORE && IEEE1394 && INPUT + help + Support for external IEEE1394 adapters designed by Digital Everywhere and + produced by El Gato, shipped under the brand name 'FireDTV/FloppyDTV'. + + These devices don't have a MPEG decoder built in, so you need + an external software decoder to watch TV. + + Say Y if you own such a device and want to use it. diff --git a/drivers/media/dvb/firesat/Makefile b/drivers/media/dvb/firesat/Makefile new file mode 100644 index 000000000000..fdf86870f1fd --- /dev/null +++ b/drivers/media/dvb/firesat/Makefile @@ -0,0 +1,12 @@ +firesat-objs := firesat_1394.o \ + firesat_dvb.o \ + firesat_fe.o \ + avc_api.o \ + cmp.o \ + firesat-rc.o \ + firesat-ci.o + +obj-$(CONFIG_DVB_FIRESAT) += firesat.o + +EXTRA_CFLAGS := -Idrivers/ieee1394 +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c new file mode 100644 index 000000000000..d70795623fb9 --- /dev/null +++ b/drivers/media/dvb/firesat/avc_api.c @@ -0,0 +1,848 @@ +/* + * FireSAT AVC driver + * + * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (c) 2008 Ben Backx <ben@bbackx.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include "firesat.h" +#include <ieee1394_transactions.h> +#include <nodemgr.h> +#include <asm/byteorder.h> +#include <linux/delay.h> +#include "avc_api.h" +#include "firesat-rc.h" + +#define RESPONSE_REGISTER 0xFFFFF0000D00ULL +#define COMMAND_REGISTER 0xFFFFF0000B00ULL +#define PCR_BASE_ADDRESS 0xFFFFF0000900ULL + +static int __AVCRegisterRemoteControl(struct firesat*firesat, int internal); + +/* Frees an allocated packet */ +static void avc_free_packet(struct hpsb_packet *packet) +{ + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); +} + +/* + * Goofy routine that basically does a down_timeout function. + * Stolen from sbp2.c + */ +static int avc_down_timeout(atomic_t *done, int timeout) +{ + int i; + + for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) { + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(HZ/10)) /* 100ms */ + return(1); + } + return ((i > 0) ? 0:1); +} + +static int __AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) { + struct hpsb_packet *packet; + struct node_entry *ne; + + ne = firesat->nodeentry; + if(!ne) { + printk("%s: lost node!\n",__func__); + return -EIO; + } + + /* need all input data */ + if(!firesat || !ne || !CmdFrm) + return -EINVAL; + +// printk(KERN_INFO "AVCWrite command %x\n",CmdFrm->opcode); + +// for(k=0;k<CmdFrm->length;k++) +// printk(KERN_INFO "CmdFrm[%d] = %08x\n", k, ((quadlet_t*)CmdFrm)[k]); + + packet=hpsb_make_writepacket(ne->host, ne->nodeid, COMMAND_REGISTER, + (quadlet_t*)CmdFrm, CmdFrm->length); + + hpsb_set_packet_complete_task(packet, (void (*)(void*))avc_free_packet, + packet); + + hpsb_node_fill_packet(ne, packet); + + if(RspFrm) + atomic_set(&firesat->avc_reply_received, 0); + + if (hpsb_send_packet(packet) < 0) { + avc_free_packet(packet); + atomic_set(&firesat->avc_reply_received, 1); + return -EIO; + } + + if(RspFrm) { + if(avc_down_timeout(&firesat->avc_reply_received,HZ/2)) { + printk("%s: timeout waiting for avc response\n",__func__); + atomic_set(&firesat->avc_reply_received, 1); + return -ETIMEDOUT; + } + + memcpy(RspFrm,firesat->respfrm,firesat->resp_length); + } + + return 0; +} + +int AVCWrite(struct firesat*firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) { + int ret; + if(down_interruptible(&firesat->avc_sem)) + return -EINTR; + + ret = __AVCWrite(firesat, CmdFrm, RspFrm); + + up(&firesat->avc_sem); + return ret; +} + +static void do_schedule_remotecontrol(unsigned long ignored); +DECLARE_TASKLET(schedule_remotecontrol, do_schedule_remotecontrol, 0); + +static void do_schedule_remotecontrol(unsigned long ignored) { + struct firesat *firesat; + unsigned long flags; + + spin_lock_irqsave(&firesat_list_lock, flags); + list_for_each_entry(firesat,&firesat_list,list) { + if(atomic_read(&firesat->reschedule_remotecontrol) == 1) { + if(down_trylock(&firesat->avc_sem)) + tasklet_schedule(&schedule_remotecontrol); + else { + if(__AVCRegisterRemoteControl(firesat, 1) == 0) + atomic_set(&firesat->reschedule_remotecontrol, 0); + else + tasklet_schedule(&schedule_remotecontrol); + + up(&firesat->avc_sem); + } + } + } + spin_unlock_irqrestore(&firesat_list_lock, flags); +} + +int AVCRecv(struct firesat *firesat, u8 *data, size_t length) { +// printk(KERN_INFO "%s\n",__func__); + + // remote control handling + + AVCRspFrm *RspFrm = (AVCRspFrm*)data; + + if(/*RspFrm->length >= 8 && ###*/ + ((RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && + RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && + RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2)) && + RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { + if(RspFrm->resp == CHANGED) { +// printk(KERN_INFO "%s: code = %02x %02x\n",__func__,RspFrm->operand[4],RspFrm->operand[5]); + firesat_got_remotecontrolcode((((u16)RspFrm->operand[4]) << 8) | ((u16)RspFrm->operand[5])); + + // schedule + atomic_set(&firesat->reschedule_remotecontrol, 1); + tasklet_schedule(&schedule_remotecontrol); + } else if(RspFrm->resp != INTERIM) + printk(KERN_INFO "%s: remote control result = %d\n",__func__, RspFrm->resp); + return 0; + } + + if(atomic_read(&firesat->avc_reply_received) == 1) { + printk("%s: received out-of-order AVC response, ignored\n",__func__); + return -EINVAL; + } +// AVCRspFrm *resp=(AVCRspFrm *)data; +// int k; +/* + printk(KERN_INFO "resp=0x%x\n",resp->resp); + printk(KERN_INFO "cts=0x%x\n",resp->cts); + printk(KERN_INFO "suid=0x%x\n",resp->suid); + printk(KERN_INFO "sutyp=0x%x\n",resp->sutyp); + printk(KERN_INFO "opcode=0x%x\n",resp->opcode); + printk(KERN_INFO "length=%d\n",resp->length); +*/ +// for(k=0;k<2;k++) +// printk(KERN_INFO "operand[%d]=%02x\n",k,resp->operand[k]); + + memcpy(firesat->respfrm,data,length); + firesat->resp_length=length; + + atomic_set(&firesat->avc_reply_received, 1); + + return 0; +} + +// tuning command for setting the relative LNB frequency (not supported by the AVC standard) +static void AVCTuner_tuneQPSK(struct firesat *firesat, struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) { + memset(CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm->cts = AVC; + CmdFrm->ctype = CONTROL; + CmdFrm->sutyp = 0x5; + CmdFrm->suid = firesat->subunit; + CmdFrm->opcode = VENDOR; + + CmdFrm->operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm->operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm->operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm->operand[3]=SFE_VENDOR_OPCODE_TUNE_QPSK; + + printk(KERN_INFO "%s: tuning to frequency %u\n",__func__,params->frequency); + + CmdFrm->operand[4] = (params->frequency >> 24) & 0xFF; + CmdFrm->operand[5] = (params->frequency >> 16) & 0xFF; + CmdFrm->operand[6] = (params->frequency >> 8) & 0xFF; + CmdFrm->operand[7] = params->frequency & 0xFF; + + printk(KERN_INFO "%s: symbol rate = %uBd\n",__func__,params->u.qpsk.symbol_rate); + + CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate/1000) >> 8) & 0xFF; + CmdFrm->operand[9] = (params->u.qpsk.symbol_rate/1000) & 0xFF; + + switch(params->u.qpsk.fec_inner) { + case FEC_1_2: + CmdFrm->operand[10] = 0x1; + break; + case FEC_2_3: + CmdFrm->operand[10] = 0x2; + break; + case FEC_3_4: + CmdFrm->operand[10] = 0x3; + break; + case FEC_5_6: + CmdFrm->operand[10] = 0x4; + break; + case FEC_7_8: + CmdFrm->operand[10] = 0x5; + break; + case FEC_4_5: + case FEC_8_9: + case FEC_AUTO: + default: + CmdFrm->operand[10] = 0x0; + } + + if(firesat->voltage == 0xff) + CmdFrm->operand[11] = 0xff; + else + CmdFrm->operand[11] = (firesat->voltage==SEC_VOLTAGE_18)?0:1; // polarisation + if(firesat->tone == 0xff) + CmdFrm->operand[12] = 0xff; + else + CmdFrm->operand[12] = (firesat->tone==SEC_TONE_ON)?1:0; // band + + CmdFrm->length = 16; +} + +int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, BYTE *status) { + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + M_VALID_FLAGS flags; + int k; + +// printk(KERN_INFO "%s\n", __func__); + + if(firesat->type == FireSAT_DVB_S) + AVCTuner_tuneQPSK(firesat, params, &CmdFrm); + else { + if(firesat->type == FireSAT_DVB_T) { + flags.Bits_T.GuardInterval = (params->u.ofdm.guard_interval != GUARD_INTERVAL_AUTO); + flags.Bits_T.CodeRateLPStream = (params->u.ofdm.code_rate_LP != FEC_AUTO); + flags.Bits_T.CodeRateHPStream = (params->u.ofdm.code_rate_HP != FEC_AUTO); + flags.Bits_T.HierarchyInfo = (params->u.ofdm.hierarchy_information != HIERARCHY_AUTO); + flags.Bits_T.Constellation = (params->u.ofdm.constellation != QAM_AUTO); + flags.Bits_T.Bandwidth = (params->u.ofdm.bandwidth != BANDWIDTH_AUTO); + flags.Bits_T.CenterFrequency = 1; + flags.Bits_T.reserved1 = 0; + flags.Bits_T.reserved2 = 0; + flags.Bits_T.OtherFrequencyFlag = 0; + flags.Bits_T.TransmissionMode = (params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO); + flags.Bits_T.NetworkId = 0; + } else { + flags.Bits.Modulation = 0; + if(firesat->type == FireSAT_DVB_S) { + flags.Bits.FEC_inner = 1; + } else if(firesat->type == FireSAT_DVB_C) { + flags.Bits.FEC_inner = 0; + } + flags.Bits.FEC_outer = 0; + flags.Bits.Symbol_Rate = 1; + flags.Bits.Frequency = 1; + flags.Bits.Orbital_Pos = 0; + if(firesat->type == FireSAT_DVB_S) { + flags.Bits.Polarisation = 1; + } else if(firesat->type == FireSAT_DVB_C) { + flags.Bits.Polarisation = 0; + } + flags.Bits.reserved_fields = 0; + flags.Bits.reserved1 = 0; + flags.Bits.Network_ID = 0; + } + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = DSD; + + CmdFrm.operand[0] = 0; // source plug + CmdFrm.operand[1] = 0xD2; // subfunction replace + CmdFrm.operand[2] = 0x20; // system id = DVB + CmdFrm.operand[3] = 0x00; // antenna number + CmdFrm.operand[4] = (firesat->type == FireSAT_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length + CmdFrm.operand[5] = flags.Valid_Word.ByteHi; // valid_flags [0] + CmdFrm.operand[6] = flags.Valid_Word.ByteLo; // valid_flags [1] + + if(firesat->type == FireSAT_DVB_T) { + CmdFrm.operand[7] = 0x0; + CmdFrm.operand[8] = (params->frequency/10) >> 24; + CmdFrm.operand[9] = ((params->frequency/10) >> 16) & 0xFF; + CmdFrm.operand[10] = ((params->frequency/10) >> 8) & 0xFF; + CmdFrm.operand[11] = (params->frequency/10) & 0xFF; + switch(params->u.ofdm.bandwidth) { + case BANDWIDTH_7_MHZ: + CmdFrm.operand[12] = 0x20; + break; + case BANDWIDTH_8_MHZ: + case BANDWIDTH_6_MHZ: // not defined by AVC spec + case BANDWIDTH_AUTO: + default: + CmdFrm.operand[12] = 0x00; + } + switch(params->u.ofdm.constellation) { + case QAM_16: + CmdFrm.operand[13] = 1 << 6; + break; + case QAM_64: + CmdFrm.operand[13] = 2 << 6; + break; + case QPSK: + default: + CmdFrm.operand[13] = 0x00; + } + switch(params->u.ofdm.hierarchy_information) { + case HIERARCHY_1: + CmdFrm.operand[13] |= 1 << 3; + break; + case HIERARCHY_2: + CmdFrm.operand[13] |= 2 << 3; + break; + case HIERARCHY_4: + CmdFrm.operand[13] |= 3 << 3; + break; + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + default: + break; + } + switch(params->u.ofdm.code_rate_HP) { + case FEC_2_3: + CmdFrm.operand[13] |= 1; + break; + case FEC_3_4: + CmdFrm.operand[13] |= 2; + break; + case FEC_5_6: + CmdFrm.operand[13] |= 3; + break; + case FEC_7_8: + CmdFrm.operand[13] |= 4; + break; + case FEC_1_2: + default: + break; + } + switch(params->u.ofdm.code_rate_LP) { + case FEC_2_3: + CmdFrm.operand[14] = 1 << 5; + break; + case FEC_3_4: + CmdFrm.operand[14] = 2 << 5; + break; + case FEC_5_6: + CmdFrm.operand[14] = 3 << 5; + break; + case FEC_7_8: + CmdFrm.operand[14] = 4 << 5; + break; + case FEC_1_2: + default: + CmdFrm.operand[14] = 0x00; + break; + } + switch(params->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_16: + CmdFrm.operand[14] |= 1 << 3; + break; + case GUARD_INTERVAL_1_8: + CmdFrm.operand[14] |= 2 << 3; + break; + case GUARD_INTERVAL_1_4: + CmdFrm.operand[14] |= 3 << 3; + break; + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + default: + break; + } + switch(params->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_8K: + CmdFrm.operand[14] |= 1 << 1; + break; + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + default: + break; + } + + CmdFrm.operand[15] = 0x00; // network_ID[0] + CmdFrm.operand[16] = 0x00; // network_ID[1] + CmdFrm.operand[17] = 0x00; // Nr_of_dsd_sel_specs = 0 - > No PIDs are transmitted + + CmdFrm.length = 20; + } else { + CmdFrm.operand[7] = 0x00; + CmdFrm.operand[8] = (((firesat->voltage==SEC_VOLTAGE_18)?0:1)<<6); /* 0 = H, 1 = V */ + CmdFrm.operand[9] = 0x00; + CmdFrm.operand[10] = 0x00; + + if(firesat->type == FireSAT_DVB_S) { + /* ### relative frequency -> absolute frequency */ + CmdFrm.operand[11] = (((params->frequency/4) >> 16) & 0xFF) | (2 << 6); + CmdFrm.operand[12] = ((params->frequency/4) >> 8) & 0xFF; + CmdFrm.operand[13] = (params->frequency/4) & 0xFF; + } else if(firesat->type == FireSAT_DVB_C) { + CmdFrm.operand[11] = (((params->frequency/4000) >> 16) & 0xFF) | (2 << 6); + CmdFrm.operand[12] = ((params->frequency/4000) >> 8) & 0xFF; + CmdFrm.operand[13] = (params->frequency/4000) & 0xFF; + } + + CmdFrm.operand[14] = ((params->u.qpsk.symbol_rate/1000) >> 12) & 0xFF; + CmdFrm.operand[15] = ((params->u.qpsk.symbol_rate/1000) >> 4) & 0xFF; + CmdFrm.operand[16] = ((params->u.qpsk.symbol_rate/1000) << 4) & 0xF0; + + CmdFrm.operand[17] = 0x00; + switch(params->u.qpsk.fec_inner) { + case FEC_1_2: + CmdFrm.operand[18] = 0x1; + break; + case FEC_2_3: + CmdFrm.operand[18] = 0x2; + break; + case FEC_3_4: + CmdFrm.operand[18] = 0x3; + break; + case FEC_5_6: + CmdFrm.operand[18] = 0x4; + break; + case FEC_7_8: + CmdFrm.operand[18] = 0x5; + break; + case FEC_4_5: + case FEC_8_9: + case FEC_AUTO: + default: + CmdFrm.operand[18] = 0x0; + } + if(firesat->type == FireSAT_DVB_S) { + CmdFrm.operand[19] = 0x08; // modulation + } else if(firesat->type == FireSAT_DVB_C) { + switch(params->u.qam.modulation) { + case QAM_16: + CmdFrm.operand[19] = 0x08; // modulation + break; + case QAM_32: + CmdFrm.operand[19] = 0x10; // modulation + break; + case QAM_64: + CmdFrm.operand[19] = 0x18; // modulation + break; + case QAM_128: + CmdFrm.operand[19] = 0x20; // modulation + break; + case QAM_256: + CmdFrm.operand[19] = 0x28; // modulation + break; + case QAM_AUTO: + default: + CmdFrm.operand[19] = 0x00; // modulation + } + } + CmdFrm.operand[20] = 0x00; + CmdFrm.operand[21] = 0x00; + CmdFrm.operand[22] = 0x00; // Nr_of_dsd_sel_specs = 0 - > No PIDs are transmitted + + CmdFrm.length=28; + } + } // AVCTuner_DSD_direct + + if((k=AVCWrite(firesat,&CmdFrm,&RspFrm))) + return k; + +// msleep(250); + mdelay(500); + + if(status) + *status=RspFrm.operand[2]; + return 0; +} + +int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) { + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int pos,k; + + printk(KERN_INFO "%s\n", __func__); + + if(pidc > 16 && pidc != 0xFF) + return -EINVAL; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = DSD; + + CmdFrm.operand[0] = 0; // source plug + CmdFrm.operand[1] = 0xD2; // subfunction replace + CmdFrm.operand[2] = 0x20; // system id = DVB + CmdFrm.operand[3] = 0x00; // antenna number + CmdFrm.operand[4] = 0x11; // system_specific_multiplex selection_length + CmdFrm.operand[5] = 0x00; // valid_flags [0] + CmdFrm.operand[6] = 0x00; // valid_flags [1] + + if(firesat->type == FireSAT_DVB_T) { +/* CmdFrm.operand[7] = 0x00; + CmdFrm.operand[8] = 0x00;//(params->frequency/10) >> 24; + CmdFrm.operand[9] = 0x00;//((params->frequency/10) >> 16) & 0xFF; + CmdFrm.operand[10] = 0x00;//((params->frequency/10) >> 8) & 0xFF; + CmdFrm.operand[11] = 0x00;//(params->frequency/10) & 0xFF; + CmdFrm.operand[12] = 0x00; + CmdFrm.operand[13] = 0x00; + CmdFrm.operand[14] = 0x00; + + CmdFrm.operand[15] = 0x00; // network_ID[0] + CmdFrm.operand[16] = 0x00; // network_ID[1] +*/ CmdFrm.operand[17] = pidc; // Nr_of_dsd_sel_specs + + pos=18; + } else { +/* CmdFrm.operand[7] = 0x00; + CmdFrm.operand[8] = 0x00; + CmdFrm.operand[9] = 0x00; + CmdFrm.operand[10] = 0x00; + + CmdFrm.operand[11] = 0x00;//(((params->frequency/4) >> 16) & 0xFF) | (2 << 6); + CmdFrm.operand[12] = 0x00;//((params->frequency/4) >> 8) & 0xFF; + CmdFrm.operand[13] = 0x00;//(params->frequency/4) & 0xFF; + + CmdFrm.operand[14] = 0x00;//((params->u.qpsk.symbol_rate/1000) >> 12) & 0xFF; + CmdFrm.operand[15] = 0x00;//((params->u.qpsk.symbol_rate/1000) >> 4) & 0xFF; + CmdFrm.operand[16] = 0x00;//((params->u.qpsk.symbol_rate/1000) << 4) & 0xF0; + + CmdFrm.operand[17] = 0x00; + CmdFrm.operand[18] = 0x00; + CmdFrm.operand[19] = 0x00; // modulation + CmdFrm.operand[20] = 0x00; + CmdFrm.operand[21] = 0x00;*/ + CmdFrm.operand[22] = pidc; // Nr_of_dsd_sel_specs + + pos=23; + } + if(pidc != 0xFF) + for(k=0;k<pidc;k++) { + CmdFrm.operand[pos++] = 0x13; // flowfunction relay + CmdFrm.operand[pos++] = 0x80; // dsd_sel_spec_valid_flags -> PID + CmdFrm.operand[pos++] = (pid[k] >> 8) & 0x1F; + CmdFrm.operand[pos++] = pid[k] & 0xFF; + CmdFrm.operand[pos++] = 0x00; // tableID + CmdFrm.operand[pos++] = 0x00; // filter_length + } + + CmdFrm.length = pos+3; + + if((pos+3)%4) + CmdFrm.length += 4 - ((pos+3)%4); + + if((k=AVCWrite(firesat,&CmdFrm,&RspFrm))) + return k; + + mdelay(250); + + return 0; +} + +int AVCTuner_GetTS(struct firesat *firesat){ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int k; + + printk(KERN_INFO "%s\n", __func__); + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = DSIT; + + CmdFrm.operand[0] = 0; // source plug + CmdFrm.operand[1] = 0xD2; // subfunction replace + CmdFrm.operand[2] = 0xFF; //status + CmdFrm.operand[3] = 0x20; // system id = DVB + CmdFrm.operand[4] = 0x00; // antenna number + CmdFrm.operand[5] = 0x0; // system_specific_search_flags + CmdFrm.operand[6] = 0x11; // system_specific_multiplex selection_length + CmdFrm.operand[7] = 0x00; // valid_flags [0] + CmdFrm.operand[8] = 0x00; // valid_flags [1] + CmdFrm.operand[24] = 0x00; // nr_of_dsit_sel_specs (always 0) + + CmdFrm.length = 28; + + if((k=AVCWrite(firesat, &CmdFrm, &RspFrm))) return k; + + mdelay(250); + return 0; +} + +int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport, int *has_ci) { + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm,0,sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; // tuner + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = READ_DESCRIPTOR; + + CmdFrm.operand[0]=DESCRIPTOR_SUBUNIT_IDENTIFIER; + CmdFrm.operand[1]=0xff; + CmdFrm.operand[2]=0x00; + CmdFrm.operand[3]=0x00; // length highbyte + CmdFrm.operand[4]=0x08; // length lowbyte + CmdFrm.operand[5]=0x00; // offset highbyte + CmdFrm.operand[6]=0x0d; // offset lowbyte + + CmdFrm.length=12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm)<0) + return -EIO; + + if(RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { + printk("%s: AVCWrite returned error code %d\n",__func__,RspFrm.resp); + return -EINVAL; + } + if(((RspFrm.operand[3] << 8) + RspFrm.operand[4]) != 8) { + printk("%s: Invalid response length\n",__func__); + return -EINVAL; + } + if(systemId) + *systemId = RspFrm.operand[7]; + if(transport) + *transport = RspFrm.operand[14] & 0x7; + switch(RspFrm.operand[14] & 0x7) { + case 1: + printk(KERN_INFO "%s: found DVB/S\n",__func__); + break; + case 2: + printk(KERN_INFO "%s: found DVB/C\n",__func__); + break; + case 3: + printk(KERN_INFO "%s: found DVB/T\n",__func__); + break; + default: + printk(KERN_INFO "%s: found unknown tuner id %u\n",__func__,RspFrm.operand[14] & 0x7); + } + if(has_ci) + *has_ci = (RspFrm.operand[14] >> 4) & 0x1; + return 0; +} + +int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_info) { + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int length; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts=AVC; + CmdFrm.ctype=CONTROL; + CmdFrm.sutyp=0x05; // tuner + CmdFrm.suid=firesat->subunit; + CmdFrm.opcode=READ_DESCRIPTOR; + + CmdFrm.operand[0]=DESCRIPTOR_TUNER_STATUS; + CmdFrm.operand[1]=0xff; + CmdFrm.operand[2]=0x00; + CmdFrm.operand[3]=sizeof(ANTENNA_INPUT_INFO) >> 8; + CmdFrm.operand[4]=sizeof(ANTENNA_INPUT_INFO) & 0xFF; + CmdFrm.operand[5]=0x00; + CmdFrm.operand[6]=0x03; + CmdFrm.length=12; + //Absenden des AVC request und warten auf response + if (AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + if(RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { + printk("%s: AVCWrite returned code %d\n",__func__,RspFrm.resp); + return -EINVAL; + } + + length = (RspFrm.operand[3] << 8) + RspFrm.operand[4]; + if(length == sizeof(ANTENNA_INPUT_INFO)) + { + memcpy(antenna_input_info,&RspFrm.operand[7],length); + return 0; + } + printk("%s: invalid info returned from AVC\n",__func__); + return -EINVAL; +} + +int AVCLNBControl(struct firesat *firesat, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int i,j; + + printk(KERN_INFO "%s: voltage = %x, burst = %x, conttone = %x\n",__func__,voltage,burst,conttone); + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts=AVC; + CmdFrm.ctype=CONTROL; + CmdFrm.sutyp=0x05; + CmdFrm.suid=firesat->subunit; + CmdFrm.opcode=VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_LNB_CONTROL; + + CmdFrm.operand[4]=voltage; + CmdFrm.operand[5]=nrdiseq; + + i=6; + + for(j=0;j<nrdiseq;j++) { + int k; + printk(KERN_INFO "%s: diseq %d len %x\n",__func__,j,diseqcmd[j].msg_len); + CmdFrm.operand[i++]=diseqcmd[j].msg_len; + + for(k=0;k<diseqcmd[j].msg_len;k++) { + printk(KERN_INFO "%s: diseq %d msg[%d] = %x\n",__func__,j,k,diseqcmd[j].msg[k]); + CmdFrm.operand[i++]=diseqcmd[j].msg[k]; + } + } + + CmdFrm.operand[i++]=burst; + CmdFrm.operand[i++]=conttone; + + CmdFrm.length=i+3; + if((i+3)%4) + CmdFrm.length += 4 - ((i+3)%4); + +/* for(j=0;j<CmdFrm.length;j++) + printk(KERN_INFO "%s: CmdFrm.operand[%d]=0x%x\n",__func__,j,CmdFrm.operand[j]); + + printk(KERN_INFO "%s: cmdfrm.length = %u\n",__func__,CmdFrm.length); + */ + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + if(RspFrm.resp != ACCEPTED) { + printk("%s: AVCWrite returned code %d\n",__func__,RspFrm.resp); + return -EINVAL; + } + + return 0; +} + +int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x1f; + CmdFrm.suid = 0x7; + CmdFrm.opcode = SUBUNIT_Info; + + CmdFrm.operand[0] = 0x07; + CmdFrm.operand[1] = 0xff; + CmdFrm.operand[2] = 0xff; + CmdFrm.operand[3] = 0xff; + CmdFrm.operand[4] = 0xff; + + CmdFrm.length = 8; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + if(RspFrm.resp != STABLE) { + printk("%s: AVCWrite returned code %d\n",__func__,RspFrm.resp); + return -EINVAL; + } + + if(subunitcount) + *subunitcount = (RspFrm.operand[1] & 0x7) + 1; + + return 0; +} + +static int __AVCRegisterRemoteControl(struct firesat*firesat, int internal) +{ + AVCCmdFrm CmdFrm; + +// printk(KERN_INFO "%s\n",__func__); + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = NOTIFY; + CmdFrm.sutyp = 0x1f; + CmdFrm.suid = 0x7; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0] = SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1] = SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2] = SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; + + CmdFrm.length = 8; + + if(internal) { + if(__AVCWrite(firesat,&CmdFrm,NULL) < 0) + return -EIO; + } else + if(AVCWrite(firesat,&CmdFrm,NULL) < 0) + return -EIO; + + return 0; +} + +int AVCRegisterRemoteControl(struct firesat*firesat) +{ + return __AVCRegisterRemoteControl(firesat, 0); +} diff --git a/drivers/media/dvb/firesat/avc_api.h b/drivers/media/dvb/firesat/avc_api.h new file mode 100644 index 000000000000..f9a190adcd37 --- /dev/null +++ b/drivers/media/dvb/firesat/avc_api.h @@ -0,0 +1,381 @@ +/*************************************************************************** + avc_api.h - description + ------------------- + begin : Wed May 1 2000 + copyright : (C) 2000 by Manfred Weihs + copyright : (C) 2003 by Philipp Gutgsell + email : 0014guph@edu.fh-kaernten.ac.at + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* + This is based on code written by Peter Halwachs, + Thomas Groiss and Andreas Monitzer. +*/ + + +#ifndef __AVC_API_H__ +#define __AVC_API_H__ + +#include <linux/dvb/frontend.h> + +#define BYTE unsigned char +#define WORD unsigned short +#define DWORD unsigned long +#define ULONG unsigned long +#define LONG long + + +/************************************************************* + FCP Address range +**************************************************************/ + +#define RESPONSE_REGISTER 0xFFFFF0000D00ULL +#define COMMAND_REGISTER 0xFFFFF0000B00ULL +#define PCR_BASE_ADDRESS 0xFFFFF0000900ULL + + +/************************************************************ + definition of structures +*************************************************************/ +typedef struct { + int Nr_SourcePlugs; + int Nr_DestinationPlugs; +} TunerInfo; + + +/*********************************************** + + supported cts + +************************************************/ + +#define AVC 0x0 + +// FCP command frame with ctype = 0x0 is AVC command frame + +#ifdef __LITTLE_ENDIAN + +// Definition FCP Command Frame +typedef struct _AVCCmdFrm +{ + // AV/C command frame + BYTE ctype : 4 ; // command type + BYTE cts : 4 ; // always 0x0 for AVC + BYTE suid : 3 ; // subunit ID + BYTE sutyp : 5 ; // subunit_typ + BYTE opcode : 8 ; // opcode + BYTE operand[509] ; // array of operands [1-507] + int length; //length of the command frame +} AVCCmdFrm ; + +// Definition FCP Response Frame +typedef struct _AVCRspFrm +{ + // AV/C response frame + BYTE resp : 4 ; // response type + BYTE cts : 4 ; // always 0x0 for AVC + BYTE suid : 3 ; // subunit ID + BYTE sutyp : 5 ; // subunit_typ + BYTE opcode : 8 ; // opcode + BYTE operand[509] ; // array of operands [1-507] + int length; //length of the response frame +} AVCRspFrm ; + +#else + +typedef struct _AVCCmdFrm +{ + BYTE cts:4; + BYTE ctype:4; + BYTE sutyp:5; + BYTE suid:3; + BYTE opcode; + BYTE operand[509]; + int length; +} AVCCmdFrm; + +typedef struct _AVCRspFrm +{ + BYTE cts:4; + BYTE resp:4; + BYTE sutyp:5; + BYTE suid:3; + BYTE opcode; + BYTE operand[509]; + int length; +} AVCRspFrm; + +#endif + +/************************************************************* + AVC command types (ctype) +**************************************************************/// +#define CONTROL 0x00 +#define STATUS 0x01 +#define INQUIRY 0x02 +#define NOTIFY 0x03 + +/************************************************************* + AVC respond types +**************************************************************/// +#define NOT_IMPLEMENTED 0x8 +#define ACCEPTED 0x9 +#define REJECTED 0xA +#define STABLE 0xC +#define CHANGED 0xD +#define INTERIM 0xF + +/************************************************************* + AVC opcodes +**************************************************************/// +#define CONNECT 0x24 +#define DISCONNECT 0x25 +#define UNIT_INFO 0x30 +#define SUBUNIT_Info 0x31 +#define VENDOR 0x00 + +#define PLUG_INFO 0x02 +#define OPEN_DESCRIPTOR 0x08 +#define READ_DESCRIPTOR 0x09 +#define OBJECT_NUMBER_SELECT 0x0D + +/************************************************************* + AVCTuner opcodes +**************************************************************/ + +#define DSIT 0xC8 +#define DSD 0xCB +#define DESCRIPTOR_TUNER_STATUS 0x80 +#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 + +/************************************************************* + AVCTuner list types +**************************************************************/ +#define Multiplex_List 0x80 +#define Service_List 0x82 + +/************************************************************* + AVCTuner object entries +**************************************************************/ +#define Multiplex 0x80 +#define Service 0x82 +#define Service_with_specified_components 0x83 +#define Preferred_components 0x90 +#define Component 0x84 + +/************************************************************* + Vendor-specific commands +**************************************************************/ + +// digital everywhere vendor ID +#define SFE_VENDOR_DE_COMPANYID_0 0x00 +#define SFE_VENDOR_DE_COMPANYID_1 0x12 +#define SFE_VENDOR_DE_COMPANYID_2 0x87 + +#define SFE_VENDOR_MAX_NR_COMPONENTS 0x4 +#define SFE_VENDOR_MAX_NR_SERVICES 0x3 +#define SFE_VENDOR_MAX_NR_DSD_ELEMENTS 0x10 + +// vendor commands +#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0A +#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 +#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 // QPSK command for DVB-S + +// TODO: following vendor specific commands needs to be implemented +#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 +#define SFE_VENDOR_OPCODE_HOST2CA 0x56 +#define SFE_VENDOR_OPCODE_CA2HOST 0x57 +#define SFE_VENDOR_OPCODE_CISTATUS 0x59 +#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 // QPSK command for DVB-S2 devices + + +//AVCTuner DVB identifier service_ID +#define DVB 0x20 + +/************************************************************* + AVC descriptor types +**************************************************************/ + +#define Subunit_Identifier_Descriptor 0x00 +#define Tuner_Status_Descriptor 0x80 + +typedef struct { + BYTE Subunit_Type; + BYTE Max_Subunit_ID; +} SUBUNIT_INFO; + +/************************************************************* + + AVCTuner DVB object IDs are 6 byte long + +**************************************************************/ + +typedef struct { + BYTE Byte0; + BYTE Byte1; + BYTE Byte2; + BYTE Byte3; + BYTE Byte4; + BYTE Byte5; +}OBJECT_ID; + +/************************************************************* + MULIPLEX Structs +**************************************************************/ +typedef struct +{ +#ifdef __LITTLE_ENDIAN + BYTE RF_frequency_hByte:6; + BYTE raster_Frequency:2;//Bit7,6 raster frequency +#else + BYTE raster_Frequency:2; + BYTE RF_frequency_hByte:6; +#endif + BYTE RF_frequency_mByte; + BYTE RF_frequency_lByte; + +}FREQUENCY; + +#ifdef __LITTLE_ENDIAN + +typedef struct +{ + BYTE Modulation :1; + BYTE FEC_inner :1; + BYTE FEC_outer :1; + BYTE Symbol_Rate :1; + BYTE Frequency :1; + BYTE Orbital_Pos :1; + BYTE Polarisation :1; + BYTE reserved_fields :1; + BYTE reserved1 :7; + BYTE Network_ID :1; + +}MULTIPLEX_VALID_FLAGS; + +typedef struct +{ + BYTE GuardInterval:1; + BYTE CodeRateLPStream:1; + BYTE CodeRateHPStream:1; + BYTE HierarchyInfo:1; + BYTE Constellation:1; + BYTE Bandwidth:1; + BYTE CenterFrequency:1; + BYTE reserved1:1; + BYTE reserved2:5; + BYTE OtherFrequencyFlag:1; + BYTE TransmissionMode:1; + BYTE NetworkId:1; +}MULTIPLEX_VALID_FLAGS_DVBT; + +#else + +typedef struct { + BYTE reserved_fields:1; + BYTE Polarisation:1; + BYTE Orbital_Pos:1; + BYTE Frequency:1; + BYTE Symbol_Rate:1; + BYTE FEC_outer:1; + BYTE FEC_inner:1; + BYTE Modulation:1; + BYTE Network_ID:1; + BYTE reserved1:7; +}MULTIPLEX_VALID_FLAGS; + +typedef struct { + BYTE reserved1:1; + BYTE CenterFrequency:1; + BYTE Bandwidth:1; + BYTE Constellation:1; + BYTE HierarchyInfo:1; + BYTE CodeRateHPStream:1; + BYTE CodeRateLPStream:1; + BYTE GuardInterval:1; + BYTE NetworkId:1; + BYTE TransmissionMode:1; + BYTE OtherFrequencyFlag:1; + BYTE reserved2:5; +}MULTIPLEX_VALID_FLAGS_DVBT; + +#endif + +typedef union { + MULTIPLEX_VALID_FLAGS Bits; + MULTIPLEX_VALID_FLAGS_DVBT Bits_T; + struct { + BYTE ByteHi; + BYTE ByteLo; + } Valid_Word; +} M_VALID_FLAGS; + +typedef struct +{ +#ifdef __LITTLE_ENDIAN + BYTE ActiveSystem; + BYTE reserved:5; + BYTE NoRF:1; + BYTE Moving:1; + BYTE Searching:1; + + BYTE SelectedAntenna:7; + BYTE Input:1; + + BYTE BER[4]; + + BYTE SignalStrength; + FREQUENCY Frequency; + + BYTE ManDepInfoLength; +#else + BYTE ActiveSystem; + BYTE Searching:1; + BYTE Moving:1; + BYTE NoRF:1; + BYTE reserved:5; + + BYTE Input:1; + BYTE SelectedAntenna:7; + + BYTE BER[4]; + + BYTE SignalStrength; + FREQUENCY Frequency; + + BYTE ManDepInfoLength; +#endif +} ANTENNA_INPUT_INFO; // 11 Byte + +#define LNBCONTROL_DONTCARE 0xff + + +extern int AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm); +extern int AVCRecv(struct firesat *firesat, u8 *data, size_t length); + +extern int AVCTuner_DSIT(struct firesat *firesat, + int Source_Plug, + struct dvb_frontend_parameters *params, + BYTE *status); + +extern int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_info); +extern int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, BYTE *status); +extern int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]); +extern int AVCTuner_GetTS(struct firesat *firesat); + +extern int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport, int *has_ci); +extern int AVCLNBControl(struct firesat *firesat, char voltage, char burst, char conttone, char nrdiseq, struct dvb_diseqc_master_cmd *diseqcmd); +extern int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount); +extern int AVCRegisterRemoteControl(struct firesat *firesat); + +#endif + diff --git a/drivers/media/dvb/firesat/cmp.c b/drivers/media/dvb/firesat/cmp.c new file mode 100644 index 000000000000..37b91f3f7ff1 --- /dev/null +++ b/drivers/media/dvb/firesat/cmp.c @@ -0,0 +1,230 @@ +#include "cmp.h" +#include <ieee1394.h> +#include <nodemgr.h> +#include <highlevel.h> +#include <ohci1394.h> +#include <hosts.h> +#include <ieee1394_core.h> +#include <ieee1394_transactions.h> +#include "avc_api.h" + +typedef struct _OPCR +{ + BYTE PTPConnCount : 6 ; // Point to point connect. counter + BYTE BrConnCount : 1 ; // Broadcast connection counter + BYTE OnLine : 1 ; // On Line + + BYTE ChNr : 6 ; // Channel number + BYTE Res : 2 ; // Reserved + + BYTE PayloadHi : 2 ; // Payoad high bits + BYTE OvhdID : 4 ; // Overhead ID + BYTE DataRate : 2 ; // Data Rate + + BYTE PayloadLo ; // Payoad low byte +} OPCR ; + +#define FIRESAT_SPEED IEEE1394_SPEED_400 + +/* hpsb_lock is being removed from the kernel-source, + * therefor we define our own 'firesat_hpsb_lock'*/ + +int send_packet_and_wait(struct hpsb_packet *packet); + +int firesat_hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t * data, quadlet_t arg) { + + struct hpsb_packet *packet; + int retval = 0; + + BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet + + packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); + if (!packet) + return -ENOMEM; + + packet->generation = generation; + retval = send_packet_and_wait(packet); + if (retval < 0) + goto hpsb_lock_fail; + + retval = hpsb_packet_success(packet); + + if (retval == 0) { + *data = packet->data[0]; + } + + hpsb_lock_fail: + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); + + return retval; +} + + +static int cmp_read(struct firesat *firesat, void *buffer, u64 addr, size_t length) { + int ret; + if(down_interruptible(&firesat->avc_sem)) + return -EINTR; + + ret = hpsb_read(firesat->host, firesat->nodeentry->nodeid, firesat->nodeentry->generation, + addr, buffer, length); + + up(&firesat->avc_sem); + return ret; +} + +static int cmp_lock(struct firesat *firesat, quadlet_t *data, u64 addr, quadlet_t arg, int ext_tcode) { + int ret; + if(down_interruptible(&firesat->avc_sem)) + return -EINTR; + + ret = firesat_hpsb_lock(firesat->host, firesat->nodeentry->nodeid, firesat->nodeentry->generation, + addr, ext_tcode, data, arg); + + up(&firesat->avc_sem); + return ret; +} + +//try establishing a point-to-point connection (may be interrupted by a busreset +int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, int iso_channel) { + unsigned int BWU; //bandwidth to allocate + + quadlet_t old_oPCR,test_oPCR = 0x0; + u64 oPCR_address=0xfffff0000904ull+(output_plug << 2); + int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4); + + printk(KERN_INFO "%s: nodeid = %d\n",__func__,firesat->nodeentry->nodeid); + + if (result < 0) { + printk("%s: cannot read oPCR\n", __func__); + return result; + } else { + printk(KERN_INFO "%s: oPCR = %08x\n",__func__,test_oPCR); + do { + OPCR *hilf= (OPCR*) &test_oPCR; + + if (!hilf->OnLine) { + printk("%s: Output offline; oPCR: %08x\n", __func__, test_oPCR); + return -EBUSY; + } else { + quadlet_t new_oPCR; + + old_oPCR=test_oPCR; + if (hilf->PTPConnCount) { + if (hilf->ChNr != iso_channel) { + printk("%s: Output plug has already connection on channel %u; cannot change it to channel %u\n",__func__,hilf->ChNr,iso_channel); + return -EBUSY; + } else + printk(KERN_INFO "%s: Overlaying existing connection; connection counter was: %u\n",__func__, hilf->PTPConnCount); + BWU=0; //we allocate no bandwidth (is this necessary?) + } else { + hilf->ChNr=iso_channel; + hilf->DataRate=FIRESAT_SPEED; + + hilf->OvhdID=0; //FIXME: that is for worst case -> optimize + BWU=hilf->OvhdID?hilf->OvhdID*32:512; + BWU += (hilf->PayloadLo + (hilf->PayloadHi << 8) +3) * (2 << (3-hilf->DataRate)); +/* if (allocate_1394_resources(iso_channel,BWU)) + { + cout << "Allocation of resources failed\n"; + return -2; + }*/ + } + + hilf->PTPConnCount++; + new_oPCR=test_oPCR; + printk(KERN_INFO "%s: trying compare_swap...\n",__func__); + printk(KERN_INFO "%s: oPCR_old: %08x, oPCR_new: %08x\n",__func__, old_oPCR, new_oPCR); + result=cmp_lock(firesat, &test_oPCR, oPCR_address, old_oPCR, 2); + + if (result < 0) { + printk("%s: cannot compare_swap oPCR\n",__func__); + return result; + } + if ((old_oPCR != test_oPCR) && (!((OPCR*) &old_oPCR)->PTPConnCount)) + { + printk("%s: change of oPCR failed -> freeing resources\n",__func__); +// hilf= (OPCR*) &new_oPCR; +// unsigned int BWU=hilf->OvhdID?hilf->OvhdID*32:512; +// BWU += (hilf->Payload+3) * (2 << (3-hilf->DataRate)); +/* if (deallocate_1394_resources(iso_channel,BWU)) + { + + cout << "Deallocation of resources failed\n"; + return -3; + }*/ + } + } + } + while (old_oPCR != test_oPCR); + } + return 0; +} + +//try breaking a point-to-point connection (may be interrupted by a busreset +int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug,int iso_channel) { + quadlet_t old_oPCR,test_oPCR; + + u64 oPCR_address=0xfffff0000904ull+(output_plug << 2); + int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4); + + printk(KERN_INFO "%s\n",__func__); + + if (result < 0) { + printk("%s: cannot read oPCR\n", __func__); + return result; + } else { + do { + OPCR *hilf= (OPCR*) &test_oPCR; + + if (!hilf->OnLine || !hilf->PTPConnCount || hilf->ChNr != iso_channel) { + printk("%s: Output plug does not have PtP-connection on that channel; oPCR: %08x\n", __func__, test_oPCR); + return -EINVAL; + } else { + quadlet_t new_oPCR; + old_oPCR=test_oPCR; + hilf->PTPConnCount--; + new_oPCR=test_oPCR; + +// printk(KERN_INFO "%s: trying compare_swap...\n", __func__); + result=cmp_lock(firesat, &test_oPCR, oPCR_address, old_oPCR, 2); + if (result < 0) { + printk("%s: cannot compare_swap oPCR\n",__func__); + return result; + } + } + + } while (old_oPCR != test_oPCR); + +/* hilf = (OPCR*) &old_oPCR; + if (hilf->PTPConnCount == 1) { // if we were the last owner of this connection + cout << "deallocating 1394 resources\n"; + unsigned int BWU=hilf->OvhdID?hilf->OvhdID*32:512; + BWU += (hilf->PayloadLo + (hilf->PayloadHi << 8) +3) * (2 << (3-hilf->DataRate)); + if (deallocate_1394_resources(iso_channel,BWU)) + { + cout << "Deallocation of resources failed\n"; + return -3; + } + }*/ + } + return 0; +} + +static void complete_packet(void *data) { + complete((struct completion *) data); +} + +int send_packet_and_wait(struct hpsb_packet *packet) { + struct completion done; + int retval; + + init_completion(&done); + hpsb_set_packet_complete_task(packet, complete_packet, &done); + retval = hpsb_send_packet(packet); + if (retval == 0) + wait_for_completion(&done); + + return retval; +} diff --git a/drivers/media/dvb/firesat/cmp.h b/drivers/media/dvb/firesat/cmp.h new file mode 100644 index 000000000000..d43fbc29f262 --- /dev/null +++ b/drivers/media/dvb/firesat/cmp.h @@ -0,0 +1,9 @@ +#ifndef __FIRESAT__CMP_H_ +#define __FIRESAT__CMP_H_ + +#include "firesat.h" + +extern int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, int iso_channel); +extern int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug,int iso_channel); + +#endif diff --git a/drivers/media/dvb/firesat/firesat-ci.c b/drivers/media/dvb/firesat/firesat-ci.c new file mode 100644 index 000000000000..862d9553c5bc --- /dev/null +++ b/drivers/media/dvb/firesat/firesat-ci.c @@ -0,0 +1,95 @@ +#include "firesat-ci.h" +#include "firesat.h" +#include "avc_api.h" + +#include <linux/dvb/ca.h> +#include <dvbdev.h> +/* +static int firesat_ca_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { + //struct firesat *firesat = (struct firesat*)((struct dvb_device*)file->private_data)->priv; + int err; + +// printk(KERN_INFO "%s: ioctl %d\n",__func__,cmd); + + switch(cmd) { + case CA_RESET: + // TODO: Needs to be implemented with new AVC Vendor commands + break; + case CA_GET_CAP: { + ca_caps_t *cap=(ca_caps_t*)parg; + cap->slot_num = 1; + cap->slot_type = CA_CI_LINK; + cap->descr_num = 1; + cap->descr_type = CA_DSS; + + err = 0; + break; + } + case CA_GET_SLOT_INFO: { + ca_slot_info_t *slot=(ca_slot_info_t*)parg; + if(slot->num == 0) { + slot->type = CA_CI | CA_CI_LINK | CA_DESCR; + slot->flags = CA_CI_MODULE_PRESENT | CA_CI_MODULE_READY; + } else { + slot->type = 0; + slot->flags = 0; + } + err = 0; + break; + } + default: + err=-EINVAL; + } + return err; +} +*/ + +static int firesat_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + //return dvb_usercopy(inode, file, cmd, arg, firesat_ca_do_ioctl); + return dvb_generic_ioctl(inode, file, cmd, arg); +} + +static int firesat_ca_io_open(struct inode *inode, struct file *file) { + printk(KERN_INFO "%s!\n",__func__); + return dvb_generic_open(inode, file); +} + +static int firesat_ca_io_release(struct inode *inode, struct file *file) { + printk(KERN_INFO "%s!\n",__func__); + return dvb_generic_release(inode, file); +} + +static unsigned int firesat_ca_io_poll(struct file *file, poll_table *wait) { +// printk(KERN_INFO "%s!\n",__func__); + return POLLIN; +} + +static struct file_operations firesat_ca_fops = { + .owner = THIS_MODULE, + .read = NULL, // There is no low level read anymore + .write = NULL, // There is no low level write anymore + .ioctl = firesat_ca_ioctl, + .open = firesat_ca_io_open, + .release = firesat_ca_io_release, + .poll = firesat_ca_io_poll, +}; + +static struct dvb_device firesat_ca = { + .priv = NULL, + .users = 1, + .readers = 1, + .writers = 1, + .fops = &firesat_ca_fops, +}; + +int firesat_ca_init(struct firesat *firesat) { + int ret = dvb_register_device(firesat->adapter, &firesat->cadev, &firesat_ca, firesat, DVB_DEVICE_CA); + if(ret) return ret; + + // avoid unnecessary delays, we're not talking to the CI yet anyways + return 0; +} + +void firesat_ca_release(struct firesat *firesat) { + dvb_unregister_device(firesat->cadev); +} diff --git a/drivers/media/dvb/firesat/firesat-ci.h b/drivers/media/dvb/firesat/firesat-ci.h new file mode 100644 index 000000000000..dafe3f0f0cc9 --- /dev/null +++ b/drivers/media/dvb/firesat/firesat-ci.h @@ -0,0 +1,9 @@ +#ifndef __FIRESAT_CA_H +#define __FIRESAT_CA_H + +#include "firesat.h" + +int firesat_ca_init(struct firesat *firesat); +void firesat_ca_release(struct firesat *firesat); + +#endif diff --git a/drivers/media/dvb/firesat/firesat-rc.c b/drivers/media/dvb/firesat/firesat-rc.c new file mode 100644 index 000000000000..e300b81008af --- /dev/null +++ b/drivers/media/dvb/firesat/firesat-rc.c @@ -0,0 +1,84 @@ +#include "firesat.h" +#include "firesat-rc.h" + +#include <linux/input.h> + +static u16 firesat_irtable[] = { + KEY_ESC, + KEY_F9, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_I, + KEY_0, + KEY_ENTER, + KEY_RED, + KEY_UP, + KEY_GREEN, + KEY_F10, + KEY_SPACE, + KEY_F11, + KEY_YELLOW, + KEY_DOWN, + KEY_BLUE, + KEY_Z, + KEY_P, + KEY_PAGEDOWN, + KEY_LEFT, + KEY_W, + KEY_RIGHT, + KEY_P, + KEY_M, + KEY_R, + KEY_V, + KEY_C, + 0 +}; + +static struct input_dev firesat_idev; + +int firesat_register_rc(void) +{ + int index; + + memset(&firesat_idev, 0, sizeof(firesat_idev)); + + firesat_idev.evbit[0] = BIT(EV_KEY); + + for (index = 0; firesat_irtable[index] != 0; index++) + set_bit(firesat_irtable[index], firesat_idev.keybit); + + return input_register_device(&firesat_idev); +} + +int firesat_unregister_rc(void) +{ + input_unregister_device(&firesat_idev); + return 0; +} + +int firesat_got_remotecontrolcode(u16 code) +{ + u16 keycode; + + if (code > 0x4500 && code < 0x4520) + keycode = firesat_irtable[code - 0x4501]; + else if (code > 0x453f && code < 0x4543) + keycode = firesat_irtable[code - 0x4521]; + else { + printk(KERN_DEBUG "%s: invalid key code 0x%04x\n", __func__, + code); + return -EINVAL; + } + + input_report_key(&firesat_idev, keycode, 1); + input_report_key(&firesat_idev, keycode, 0); + + return 0; +} diff --git a/drivers/media/dvb/firesat/firesat-rc.h b/drivers/media/dvb/firesat/firesat-rc.h new file mode 100644 index 000000000000..e89a8069ba88 --- /dev/null +++ b/drivers/media/dvb/firesat/firesat-rc.h @@ -0,0 +1,9 @@ +#ifndef __FIRESAT_LIRC_H +#define __FIRESAT_LIRC_H + +extern int firesat_register_rc(void); +extern int firesat_unregister_rc(void); +extern int firesat_got_remotecontrolcode(u16 code); + +#endif + diff --git a/drivers/media/dvb/firesat/firesat.h b/drivers/media/dvb/firesat/firesat.h new file mode 100644 index 000000000000..f852a1ac7740 --- /dev/null +++ b/drivers/media/dvb/firesat/firesat.h @@ -0,0 +1,85 @@ +#ifndef __FIRESAT_H +#define __FIRESAT_H + +#include "dvb_frontend.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_net.h" + +#include <linux/semaphore.h> +#include <linux/dvb/frontend.h> +#include <linux/dvb/dmx.h> + +enum model_type { + FireSAT_DVB_S = 1, + FireSAT_DVB_C = 2, + FireSAT_DVB_T = 3 +}; + +struct firesat { + struct dvb_demux dvb_demux; + char *model_name; + + /* DVB bits */ + struct dvb_adapter *adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dmx_frontend frontend; + struct dvb_net dvbnet; + struct dvb_frontend_info *frontend_info; + struct dvb_frontend *fe; + + struct dvb_device *cadev; + int has_ci; + + struct semaphore avc_sem; + atomic_t avc_reply_received; + + atomic_t reschedule_remotecontrol; + + struct firesat_channel { + struct firesat *firesat; + struct dvb_demux_feed *dvbdmxfeed; + + int active; + int id; + int pid; + int type; /* 1 - TS, 2 - Filter */ + } channel[16]; + struct semaphore demux_sem; + + /* needed by avc_api */ + void *respfrm; + int resp_length; + +// nodeid_t nodeid; + struct hpsb_host *host; + u64 guid; /* GUID of this node */ + u32 guid_vendor_id; /* Top 24bits of guid */ + struct node_entry *nodeentry; + + enum model_type type; + char subunit; + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t tone; + + int isochannel; + + struct list_head list; +}; + +extern struct list_head firesat_list; +extern spinlock_t firesat_list_lock; + +/* firesat_dvb.c */ +extern int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed); +extern int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +extern int firesat_dvbdev_init(struct firesat *firesat, + struct device *dev, + struct dvb_frontend *fe); + +/* firesat_fe.c */ +extern int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe); + + +#endif diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c new file mode 100644 index 000000000000..c7ccf633c24b --- /dev/null +++ b/drivers/media/dvb/firesat/firesat_1394.c @@ -0,0 +1,468 @@ +/* + * FireSAT DVB driver + * + * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (c) 2007-2008 Ben Backx <ben@bbackx.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/semaphore.h> +#include <ieee1394_hotplug.h> +#include <nodemgr.h> +#include <highlevel.h> +#include <ohci1394.h> +#include <hosts.h> +#include <dvbdev.h> + +#include "firesat.h" +#include "avc_api.h" +#include "cmp.h" +#include "firesat-rc.h" +#include "firesat-ci.h" + +#define FIRESAT_Vendor_ID 0x001287 + +static struct ieee1394_device_id firesat_id_table[] = { + + { + /* FloppyDTV S/CI and FloppyDTV S2 */ + .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, + .model_id = 0x000024, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + },{ + /* FloppyDTV T/CI */ + .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, + .model_id = 0x000025, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + },{ + /* FloppyDTV C/CI */ + .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, + .model_id = 0x000026, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + },{ + /* FireDTV S/CI and FloppyDTV S2 */ + .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, + .model_id = 0x000034, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + },{ + /* FireDTV T/CI */ + .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, + .model_id = 0x000035, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + },{ + /* FireDTV C/CI */ + .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, + .model_id = 0x000036, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + }, { } +}; + +MODULE_DEVICE_TABLE(ieee1394, firesat_id_table); + +/* list of all firesat devices */ +LIST_HEAD(firesat_list); +spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED; + +static void firesat_add_host(struct hpsb_host *host); +static void firesat_remove_host(struct hpsb_host *host); +static void firesat_host_reset(struct hpsb_host *host); + +/* +static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data, + size_t length); +*/ + +static void fcp_request(struct hpsb_host *host, + int nodeid, + int direction, + int cts, + u8 *data, + size_t length); + +static struct hpsb_highlevel firesat_highlevel = { + .name = "FireSAT", + .add_host = firesat_add_host, + .remove_host = firesat_remove_host, + .host_reset = firesat_host_reset, +// FIXME .iso_receive = iso_receive, + .fcp_request = fcp_request, +}; + +static void firesat_add_host (struct hpsb_host *host) +{ + struct ti_ohci *ohci = (struct ti_ohci *)host->hostdata; + + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + if (!hpsb_create_hostinfo(&firesat_highlevel, host, 0)) { + printk(KERN_ERR "Cannot allocate hostinfo\n"); + return; + } + + hpsb_set_hostinfo(&firesat_highlevel, host, ohci); + hpsb_set_hostinfo_key(&firesat_highlevel, host, ohci->host->id); +} + +static void firesat_remove_host (struct hpsb_host *host) +{ + +} + +static void firesat_host_reset(struct hpsb_host *host) +{ + printk(KERN_INFO "FireSAT host_reset (nodeid = 0x%x, hosts active = %d)\n",host->node_id,host->nodes_active); +} + +struct firewireheader { + union { + struct { + unsigned char tcode:4; + unsigned char sy:4; + unsigned char tag:2; + unsigned char channel:6; + + unsigned char length_l; + unsigned char length_h; + } hdr; + unsigned long val; + }; +}; + +struct CIPHeader { + union { + struct { + unsigned char syncbits:2; + unsigned char sid:6; + unsigned char dbs; + unsigned char fn:2; + unsigned char qpc:3; + unsigned char sph:1; + unsigned char rsv:2; + unsigned char dbc; + unsigned char syncbits2:2; + unsigned char fmt:6; + unsigned long fdf:24; + } cip; + unsigned long long val; + }; +}; + +struct MPEG2Header { + union { + struct { + unsigned char sync; // must be 0x47 + unsigned char transport_error_indicator:1; + unsigned char payload_unit_start_indicator:1; + unsigned char transport_priority:1; + unsigned short pid:13; + unsigned char transport_scrambling_control:2; + unsigned char adaption_field_control:2; + unsigned char continuity_counter:4; + } hdr; + unsigned long val; + }; +}; + +#if 0 +static void iso_receive(struct hpsb_host *host, + int channel, + quadlet_t *data, + size_t length) +{ + struct firesat *firesat = NULL; + struct firesat *firesat_entry; + unsigned long flags; + +// printk(KERN_INFO "FireSAT iso_receive: channel %d, length = %d\n", channel, length); + + if (length <= 12) + return; // ignore empty packets + else { + + spin_lock_irqsave(&firesat_list_lock, flags); + list_for_each_entry(firesat_entry,&firesat_list,list) { + if(firesat_entry->host == host && firesat_entry->isochannel == channel) { + firesat=firesat_entry; + break; + } + } + spin_unlock_irqrestore(&firesat_list_lock, flags); + + if (firesat) { + char *buf= ((char*)data) + sizeof(struct firewireheader)+sizeof(struct CIPHeader); + int count = (length-sizeof(struct CIPHeader)) / 192; + +// printk(KERN_INFO "%s: length = %u\n data[0] = %08x\n data[1] = %08x\n data[2] = %08x\n data[3] = %08x\n data[4] = %08x\n",__func__, length, data[0],data[1],data[2],data[3],data[4]); + + while (count--) { + + if (buf[sizeof(quadlet_t) /*timestamp*/] == 0x47) + dvb_dmx_swfilter_packets(&firesat->demux, &buf[sizeof(quadlet_t)], 1); + else + printk("%s: invalid packet, skipping\n", __func__); + buf += 188 + sizeof (quadlet_t) /* timestamp */; + } + } + } +} +#endif + +static void fcp_request(struct hpsb_host *host, + int nodeid, + int direction, + int cts, + u8 *data, + size_t length) +{ + struct firesat *firesat = NULL; + struct firesat *firesat_entry; + unsigned long flags; + + if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { + + spin_lock_irqsave(&firesat_list_lock, flags); + list_for_each_entry(firesat_entry,&firesat_list,list) { + if (firesat_entry->host == host && + firesat_entry->nodeentry->nodeid == nodeid && + (firesat_entry->subunit == (data[1]&0x7) || + (firesat_entry->subunit == 0 && + (data[1]&0x7) == 0x7))) { + firesat=firesat_entry; + break; + } + } + spin_unlock_irqrestore(&firesat_list_lock, flags); + + if (firesat) + AVCRecv(firesat,data,length); + else + printk("%s: received fcp request from unknown source, ignored\n", __func__); + } // else ignore +} + +static int firesat_probe(struct device *dev) +{ + struct unit_directory *ud = container_of(dev, struct unit_directory, device); + struct firesat *firesat; + struct dvb_frontend *fe; + unsigned long flags; + int result; + unsigned char subunitcount = 0xff, subunit; + struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL); + + if (!firesats) { + printk("%s: couldn't allocate memory.\n", __func__); + return -ENOMEM; + } + +// printk(KERN_INFO "FireSAT: Detected device with GUID %08lx%04lx%04lx\n",(unsigned long)((ud->ne->guid)>>32),(unsigned long)(ud->ne->guid & 0xFFFF),(unsigned long)ud->ne->guid_vendor_id); + printk(KERN_INFO "%s: loading device\n", __func__); + + firesats[0] = NULL; + firesats[1] = NULL; + + ud->device.driver_data = firesats; + + for (subunit = 0; subunit < subunitcount; subunit++) { + + if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) || + !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) { + + printk("%s: couldn't allocate memory.\n", __func__); + kfree(firesats); + return -ENOMEM; + } + + memset(firesat, 0, sizeof (struct firesat)); + + firesat->host = ud->ne->host; + firesat->guid = ud->ne->guid; + firesat->guid_vendor_id = ud->ne->guid_vendor_id; + firesat->nodeentry = ud->ne; + firesat->isochannel = -1; + firesat->tone = 0xff; + firesat->voltage = 0xff; + + if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) { + printk("%s: couldn't allocate memory.\n", __func__); + kfree(firesat); + return -ENOMEM; + } + + sema_init(&firesat->avc_sem, 1); + atomic_set(&firesat->avc_reply_received, 1); + sema_init(&firesat->demux_sem, 1); + atomic_set(&firesat->reschedule_remotecontrol, 0); + + spin_lock_irqsave(&firesat_list_lock, flags); + INIT_LIST_HEAD(&firesat->list); + list_add_tail(&firesat->list, &firesat_list); + spin_unlock_irqrestore(&firesat_list_lock, flags); + + if (subunit == 0) { + firesat->subunit = 0x7; // 0x7 = don't care + if (AVCSubUnitInfo(firesat, &subunitcount)) { + printk("%s: AVC subunit info command failed.\n",__func__); + spin_lock_irqsave(&firesat_list_lock, flags); + list_del(&firesat->list); + spin_unlock_irqrestore(&firesat_list_lock, flags); + kfree(firesat); + return -EIO; + } + } + + printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount); + + firesat->subunit = subunit; + + if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type, &firesat->has_ci)) { + printk("%s: cannot identify subunit %d\n", __func__, subunit); + spin_lock_irqsave(&firesat_list_lock, flags); + list_del(&firesat->list); + spin_unlock_irqrestore(&firesat_list_lock, flags); + kfree(firesat); + continue; + } + +// ---- + firesat_dvbdev_init(firesat, dev, fe); +// ---- + firesats[subunit] = firesat; + } // loop for all tuners + + //beta ;-) Disable remote control stuff to avoid crashing + //if(firesats[0]) + // AVCRegisterRemoteControl(firesats[0]); + + return 0; +} + +static int firesat_remove(struct device *dev) +{ + struct unit_directory *ud = container_of(dev, struct unit_directory, device); + struct dvb_frontend* fe; + struct firesat **firesats = ud->device.driver_data; + int k; + unsigned long flags; + + if (firesats) { + for (k = 0; k < 2; k++) + if (firesats[k]) { + if (firesats[k]->has_ci) + firesat_ca_release(firesats[k]); + +#if 0 + if (!(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) { + fe->ops = firesat_ops; + fe->dvb = firesats[k]->adapter; + + dvb_unregister_frontend(fe); + kfree(fe); + } +#endif + dvb_net_release(&firesats[k]->dvbnet); + firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx); + firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend); + dvb_dmxdev_release(&firesats[k]->dmxdev); + dvb_dmx_release(&firesats[k]->demux); + dvb_unregister_adapter(firesats[k]->adapter); + + spin_lock_irqsave(&firesat_list_lock, flags); + list_del(&firesats[k]->list); + spin_unlock_irqrestore(&firesat_list_lock, flags); + + kfree(firesats[k]->adapter); + kfree(firesats[k]->respfrm); + kfree(firesats[k]); + } + kfree(firesats); + } else + printk("%s: can't get firesat handle\n", __func__); + + printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id); + + return 0; +} + +static int firesat_update(struct unit_directory *ud) +{ + struct firesat **firesats = ud->device.driver_data; + int k; + // loop over subunits + + for (k = 0; k < 2; k++) + if (firesats[k]) { + firesats[k]->nodeentry = ud->ne; + + if (firesats[k]->isochannel >= 0) + try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel); + } + + return 0; +} + +static struct hpsb_protocol_driver firesat_driver = { + + .name = "FireSAT", + .id_table = firesat_id_table, + .update = firesat_update, + + .driver = { + //.name and .bus are filled in for us in more recent linux versions + //.name = "FireSAT", + //.bus = &ieee1394_bus_type, + .probe = firesat_probe, + .remove = firesat_remove, + }, +}; + +static int __init firesat_init(void) +{ + int ret; + + printk(KERN_INFO "FireSAT loaded\n"); + hpsb_register_highlevel(&firesat_highlevel); + ret = hpsb_register_protocol(&firesat_driver); + if (ret) { + printk(KERN_ERR "FireSAT: failed to register protocol\n"); + hpsb_unregister_highlevel(&firesat_highlevel); + return ret; + } + + //Crash in this function, just disable RC for the time being... + //Don't forget to uncomment in firesat_exit and firesat_probe when you enable this. + /*if((ret=firesat_register_rc())) + printk("%s: firesat_register_rc return error code %d (ignored)\n", __func__, ret);*/ + + return 0; +} + +static void __exit firesat_exit(void) +{ + hpsb_unregister_protocol(&firesat_driver); + hpsb_unregister_highlevel(&firesat_highlevel); + printk(KERN_INFO "FireSAT quit\n"); +} + +module_init(firesat_init); +module_exit(firesat_exit); + +MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); +MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); +MODULE_DESCRIPTION("FireSAT DVB Driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FireSAT DVB"); diff --git a/drivers/media/dvb/firesat/firesat_dvb.c b/drivers/media/dvb/firesat/firesat_dvb.c new file mode 100644 index 000000000000..38aad0812881 --- /dev/null +++ b/drivers/media/dvb/firesat/firesat_dvb.c @@ -0,0 +1,350 @@ +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/semaphore.h> +#include <ieee1394_hotplug.h> +#include <nodemgr.h> +#include <highlevel.h> +#include <ohci1394.h> +#include <hosts.h> +#include <dvbdev.h> + +#include "firesat.h" +#include "avc_api.h" +#include "cmp.h" +#include "firesat-rc.h" +#include "firesat-ci.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct firesat_channel *firesat_channel_allocate(struct firesat *firesat) +{ + int k; + + printk(KERN_INFO "%s\n", __func__); + + if (down_interruptible(&firesat->demux_sem)) + return NULL; + + for (k = 0; k < 16; k++) { + printk(KERN_INFO "%s: channel %d: active = %d, pid = 0x%x\n",__func__,k,firesat->channel[k].active,firesat->channel[k].pid); + + if (firesat->channel[k].active == 0) { + firesat->channel[k].active = 1; + up(&firesat->demux_sem); + return &firesat->channel[k]; + } + } + + up(&firesat->demux_sem); + return NULL; // no more channels available +} + +static int firesat_channel_collect(struct firesat *firesat, int *pidc, u16 pid[]) +{ + int k, l = 0; + + if (down_interruptible(&firesat->demux_sem)) + return -EINTR; + + for (k = 0; k < 16; k++) + if (firesat->channel[k].active == 1) + pid[l++] = firesat->channel[k].pid; + + up(&firesat->demux_sem); + + *pidc = l; + + return 0; +} + +static int firesat_channel_release(struct firesat *firesat, + struct firesat_channel *channel) +{ + if (down_interruptible(&firesat->demux_sem)) + return -EINTR; + + channel->active = 0; + + up(&firesat->demux_sem); + return 0; +} + +int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct firesat *firesat = (struct firesat*)dvbdmxfeed->demux->priv; + struct firesat_channel *channel; + int pidc,k; + u16 pids[16]; + + printk(KERN_INFO "%s (pid %u)\n",__func__,dvbdmxfeed->pid); + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + case DMX_TYPE_SEC: + break; + default: + printk("%s: invalid type %u\n",__func__,dvbdmxfeed->type); + return -EINVAL; + } + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + switch (dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + case DMX_TS_PES_AUDIO: + case DMX_TS_PES_TELETEXT: + case DMX_TS_PES_PCR: + case DMX_TS_PES_OTHER: + //Dirty fix to keep firesat->channel pid-list up to date + for(k=0;k<16;k++){ + if(firesat->channel[k].active == 0) + firesat->channel[k].pid = + dvbdmxfeed->pid; + break; + } + channel = firesat_channel_allocate(firesat); + break; + default: + printk("%s: invalid pes type %u\n",__func__, dvbdmxfeed->pes_type); + return -EINVAL; + } + } else { + channel = firesat_channel_allocate(firesat); + } + + if (!channel) { + printk("%s: busy!\n", __func__); + return -EBUSY; + } + + dvbdmxfeed->priv = channel; + + channel->dvbdmxfeed = dvbdmxfeed; + channel->pid = dvbdmxfeed->pid; + channel->type = dvbdmxfeed->type; + channel->firesat = firesat; + + if (firesat_channel_collect(firesat, &pidc, pids)) { + firesat_channel_release(firesat, channel); + return -EINTR; + } + + if(dvbdmxfeed->pid == 8192) { + if((k=AVCTuner_GetTS(firesat))) { + firesat_channel_release(firesat, channel); + printk("%s: AVCTuner_GetTS failed with error %d\n", + __func__,k); + return k; + } + } + else { + if((k=AVCTuner_SetPIDs(firesat, pidc, pids))) { + firesat_channel_release(firesat, channel); + printk("%s: AVCTuner_SetPIDs failed with error %d\n", + __func__,k); + return k; + } + } + + return 0; +} + +int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *demux = dvbdmxfeed->demux; + struct firesat *firesat = (struct firesat*)demux->priv; + int k, l = 0; + u16 pids[16]; + + printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid); + + if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE))) { + + if (dvbdmxfeed->ts_type & TS_DECODER) { + + if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || + !demux->pesfilter[dvbdmxfeed->pes_type]) + + return -EINVAL; + + demux->pids[dvbdmxfeed->pes_type] |= 0x8000; + demux->pesfilter[dvbdmxfeed->pes_type] = 0; + } + + if (!(dvbdmxfeed->ts_type & TS_DECODER && + dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) + + return 0; + } + + if (down_interruptible(&firesat->demux_sem)) + return -EINTR; + + + // list except channel to be removed + for (k = 0; k < 16; k++) + if (firesat->channel[k].active == 1) + if (&firesat->channel[k] != + (struct firesat_channel *)dvbdmxfeed->priv) + pids[l++] = firesat->channel[k].pid; + else + firesat->channel[k].active = 0; + + if ((k = AVCTuner_SetPIDs(firesat, l, pids))) { + up(&firesat->demux_sem); + return k; + } + + ((struct firesat_channel *)dvbdmxfeed->priv)->active = 0; + + up(&firesat->demux_sem); + + return 0; +} + +int firesat_dvbdev_init(struct firesat *firesat, + struct device *dev, + struct dvb_frontend *fe) +{ + int result; + + firesat->has_ci = 1; // TEMP workaround + +#if 0 + switch (firesat->type) { + case FireSAT_DVB_S: + firesat->model_name = "FireSAT DVB-S"; + firesat->frontend_info = &firesat_S_frontend_info; + break; + case FireSAT_DVB_C: + firesat->model_name = "FireSAT DVB-C"; + firesat->frontend_info = &firesat_C_frontend_info; + break; + case FireSAT_DVB_T: + firesat->model_name = "FireSAT DVB-T"; + firesat->frontend_info = &firesat_T_frontend_info; + break; + default: + printk("%s: unknown model type 0x%x on subunit %d!\n", + __func__, firesat->type,subunit); + firesat->model_name = "Unknown"; + firesat->frontend_info = NULL; + } +#endif +/* // ------- CRAP ----------- + if (!firesat->frontend_info) { + spin_lock_irqsave(&firesat_list_lock, flags); + list_del(&firesat->list); + spin_unlock_irqrestore(&firesat_list_lock, flags); + kfree(firesat); + continue; + } +*/ + //initialising firesat->adapter before calling dvb_register_adapter + if (!(firesat->adapter = kmalloc(sizeof (struct dvb_adapter), GFP_KERNEL))) { + printk("%s: couldn't allocate memory.\n", __func__); + kfree(firesat->adapter); + kfree(firesat); + return -ENOMEM; + } + + if ((result = dvb_register_adapter(firesat->adapter, + firesat->model_name, + THIS_MODULE, + dev, adapter_nr)) < 0) { + + printk("%s: dvb_register_adapter failed: error %d\n", __func__, result); +#if 0 + /* ### cleanup */ + spin_lock_irqsave(&firesat_list_lock, flags); + list_del(&firesat->list); + spin_unlock_irqrestore(&firesat_list_lock, flags); +#endif + kfree(firesat); + + return result; + } + + firesat->demux.dmx.capabilities = 0/*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/; + + firesat->demux.priv = (void *)firesat; + firesat->demux.filternum = 16; + firesat->demux.feednum = 16; + firesat->demux.start_feed = firesat_start_feed; + firesat->demux.stop_feed = firesat_stop_feed; + firesat->demux.write_to_decoder = NULL; + + if ((result = dvb_dmx_init(&firesat->demux)) < 0) { + printk("%s: dvb_dmx_init failed: error %d\n", __func__, + result); + + dvb_unregister_adapter(firesat->adapter); + + return result; + } + + firesat->dmxdev.filternum = 16; + firesat->dmxdev.demux = &firesat->demux.dmx; + firesat->dmxdev.capabilities = 0; + + if ((result = dvb_dmxdev_init(&firesat->dmxdev, firesat->adapter)) < 0) { + printk("%s: dvb_dmxdev_init failed: error %d\n", + __func__, result); + + dvb_dmx_release(&firesat->demux); + dvb_unregister_adapter(firesat->adapter); + + return result; + } + + firesat->frontend.source = DMX_FRONTEND_0; + + if ((result = firesat->demux.dmx.add_frontend(&firesat->demux.dmx, + &firesat->frontend)) < 0) { + printk("%s: dvb_dmx_init failed: error %d\n", __func__, + result); + + dvb_dmxdev_release(&firesat->dmxdev); + dvb_dmx_release(&firesat->demux); + dvb_unregister_adapter(firesat->adapter); + + return result; + } + + if ((result = firesat->demux.dmx.connect_frontend(&firesat->demux.dmx, + &firesat->frontend)) < 0) { + printk("%s: dvb_dmx_init failed: error %d\n", __func__, + result); + + firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, &firesat->frontend); + dvb_dmxdev_release(&firesat->dmxdev); + dvb_dmx_release(&firesat->demux); + dvb_unregister_adapter(firesat->adapter); + + return result; + } + + dvb_net_init(firesat->adapter, &firesat->dvbnet, &firesat->demux.dmx); + +// fe->ops = firesat_ops; +// fe->dvb = firesat->adapter; + firesat_frontend_attach(firesat, fe); + + fe->sec_priv = firesat; //IMPORTANT, functions depend on this!!! + if ((result= dvb_register_frontend(firesat->adapter, fe)) < 0) { + printk("%s: dvb_register_frontend_new failed: error %d\n", __func__, result); + /* ### cleanup */ + return result; + } + + if (firesat->has_ci) + firesat_ca_init(firesat); + + return 0; +} diff --git a/drivers/media/dvb/firesat/firesat_fe.c b/drivers/media/dvb/firesat/firesat_fe.c new file mode 100644 index 000000000000..f7abd38f0014 --- /dev/null +++ b/drivers/media/dvb/firesat/firesat_fe.c @@ -0,0 +1,263 @@ +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/semaphore.h> +#include <ieee1394_hotplug.h> +#include <nodemgr.h> +#include <highlevel.h> +#include <ohci1394.h> +#include <hosts.h> +#include <dvbdev.h> + +#include "firesat.h" +#include "avc_api.h" +#include "cmp.h" +#include "firesat-rc.h" +#include "firesat-ci.h" + +static int firesat_dvb_init(struct dvb_frontend *fe) +{ + struct firesat *firesat = fe->sec_priv; + printk("fdi: 1\n"); + firesat->isochannel = firesat->adapter->num; //<< 1 | (firesat->subunit & 0x1); // ### ask IRM + printk("fdi: 2\n"); + try_CMPEstablishPPconnection(firesat, firesat->subunit, firesat->isochannel); + printk("fdi: 3\n"); +//FIXME hpsb_listen_channel(&firesat_highlevel, firesat->host, firesat->isochannel); + printk("fdi: 4\n"); + return 0; +} + +static int firesat_sleep(struct dvb_frontend *fe) +{ + struct firesat *firesat = fe->sec_priv; + +//FIXME hpsb_unlisten_channel(&firesat_highlevel, firesat->host, firesat->isochannel); + try_CMPBreakPPconnection(firesat, firesat->subunit, firesat->isochannel); + firesat->isochannel = -1; + return 0; +} + +static int firesat_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct firesat *firesat = fe->sec_priv; + + return AVCLNBControl(firesat, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, + LNBCONTROL_DONTCARE, 1, cmd); +} + +static int firesat_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t minicmd) +{ + return 0; +} + +static int firesat_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct firesat *firesat = fe->sec_priv; + + firesat->tone = tone; + return 0; +} + +static int firesat_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct firesat *firesat = fe->sec_priv; + + firesat->voltage = voltage; + return 0; +} + +static int firesat_read_status (struct dvb_frontend *fe, fe_status_t *status) +{ + struct firesat *firesat = fe->sec_priv; + ANTENNA_INPUT_INFO info; + + if (AVCTunerStatus(firesat, &info)) + return -EINVAL; + + if (info.NoRF) + *status = 0; + else + *status = *status = FE_HAS_SIGNAL | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_CARRIER | + FE_HAS_LOCK; + + return 0; +} + +static int firesat_read_ber (struct dvb_frontend *fe, u32 *ber) +{ + struct firesat *firesat = fe->sec_priv; + ANTENNA_INPUT_INFO info; + + if (AVCTunerStatus(firesat, &info)) + return -EINVAL; + + *ber = ((info.BER[0] << 24) & 0xff) | + ((info.BER[1] << 16) & 0xff) | + ((info.BER[2] << 8) & 0xff) | + (info.BER[3] & 0xff); + + return 0; +} + +static int firesat_read_signal_strength (struct dvb_frontend *fe, u16 *strength) +{ + struct firesat *firesat = fe->sec_priv; + ANTENNA_INPUT_INFO info; + u16 *signal = strength; + + if (AVCTunerStatus(firesat, &info)) + return -EINVAL; + + *signal = info.SignalStrength; + + return 0; +} + +static int firesat_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + return -EOPNOTSUPP; +} + +static int firesat_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + return -EOPNOTSUPP; +} + +static int firesat_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct firesat *firesat = fe->sec_priv; + + if (AVCTuner_DSD(firesat, params, NULL) != ACCEPTED) + return -EINVAL; + else + return 0; //not sure of this... +} + +static int firesat_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + return -EOPNOTSUPP; +} + +static struct dvb_frontend_info firesat_S_frontend_info; +static struct dvb_frontend_info firesat_C_frontend_info; +static struct dvb_frontend_info firesat_T_frontend_info; + +static struct dvb_frontend_ops firesat_ops = { + + .init = firesat_dvb_init, + .sleep = firesat_sleep, + + .set_frontend = firesat_set_frontend, + .get_frontend = firesat_get_frontend, + + .read_status = firesat_read_status, + .read_ber = firesat_read_ber, + .read_signal_strength = firesat_read_signal_strength, + .read_snr = firesat_read_snr, + .read_ucblocks = firesat_read_uncorrected_blocks, + + .diseqc_send_master_cmd = firesat_diseqc_send_master_cmd, + .diseqc_send_burst = firesat_diseqc_send_burst, + .set_tone = firesat_set_tone, + .set_voltage = firesat_set_voltage, +}; + +int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe) +{ + switch (firesat->type) { + case FireSAT_DVB_S: + firesat->model_name = "FireSAT DVB-S"; + firesat->frontend_info = &firesat_S_frontend_info; + break; + case FireSAT_DVB_C: + firesat->model_name = "FireSAT DVB-C"; + firesat->frontend_info = &firesat_C_frontend_info; + break; + case FireSAT_DVB_T: + firesat->model_name = "FireSAT DVB-T"; + firesat->frontend_info = &firesat_T_frontend_info; + break; + default: +// printk("%s: unknown model type 0x%x on subunit %d!\n", +// __func__, firesat->type,subunit); + printk("%s: unknown model type 0x%x !\n", + __func__, firesat->type); + firesat->model_name = "Unknown"; + firesat->frontend_info = NULL; + } + fe->ops = firesat_ops; + fe->dvb = firesat->adapter; + + return 0; +} + +static struct dvb_frontend_info firesat_S_frontend_info = { + + .name = "FireSAT DVB-S Frontend", + .type = FE_QPSK, + + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, + .symbol_rate_min = 1000000, + .symbol_rate_max = 40000000, + + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK, +}; + +static struct dvb_frontend_info firesat_C_frontend_info = { + + .name = "FireSAT DVB-C Frontend", + .type = FE_QAM, + + .frequency_min = 47000000, + .frequency_max = 866000000, + .frequency_stepsize = 62500, + .symbol_rate_min = 870000, + .symbol_rate_max = 6900000, + + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO, +}; + +static struct dvb_frontend_info firesat_T_frontend_info = { + + .name = "FireSAT DVB-T Frontend", + .type = FE_OFDM, + + .frequency_min = 49000000, + .frequency_max = 861000000, + .frequency_stepsize = 62500, + + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_2_3 | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, +}; -- cgit v1.2.3 From f1bbb43a667067f24a729df78dc050348b1c7846 Mon Sep 17 00:00:00 2001 From: Ben Backx <ben@bbackx.com> Date: Sun, 22 Jun 2008 16:00:53 +0200 Subject: firesat: fix DVB-S2 device recognition This only makes sure that a DVB-S2 device is really recognized as a S2, nothing else is added yet. It's using the string containing the model that is stored in the configuration ROM, the older version was using some hardware revision dependent part of the ROM. Signed-off-by: Ben Backx <ben@bbackx.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/avc_api.c | 17 +---------------- drivers/media/dvb/firesat/firesat.h | 3 ++- drivers/media/dvb/firesat/firesat_1394.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c index d70795623fb9..0ad6420e342f 100644 --- a/drivers/media/dvb/firesat/avc_api.c +++ b/drivers/media/dvb/firesat/avc_api.c @@ -251,7 +251,7 @@ int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params // printk(KERN_INFO "%s\n", __func__); - if(firesat->type == FireSAT_DVB_S) + if (firesat->type == FireSAT_DVB_S || firesat->type == FireSAT_DVB_S2) AVCTuner_tuneQPSK(firesat, params, &CmdFrm); else { if(firesat->type == FireSAT_DVB_T) { @@ -654,21 +654,6 @@ int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *tr } if(systemId) *systemId = RspFrm.operand[7]; - if(transport) - *transport = RspFrm.operand[14] & 0x7; - switch(RspFrm.operand[14] & 0x7) { - case 1: - printk(KERN_INFO "%s: found DVB/S\n",__func__); - break; - case 2: - printk(KERN_INFO "%s: found DVB/C\n",__func__); - break; - case 3: - printk(KERN_INFO "%s: found DVB/T\n",__func__); - break; - default: - printk(KERN_INFO "%s: found unknown tuner id %u\n",__func__,RspFrm.operand[14] & 0x7); - } if(has_ci) *has_ci = (RspFrm.operand[14] >> 4) & 0x1; return 0; diff --git a/drivers/media/dvb/firesat/firesat.h b/drivers/media/dvb/firesat/firesat.h index f852a1ac7740..d1e2ce37063e 100644 --- a/drivers/media/dvb/firesat/firesat.h +++ b/drivers/media/dvb/firesat/firesat.h @@ -13,7 +13,8 @@ enum model_type { FireSAT_DVB_S = 1, FireSAT_DVB_C = 2, - FireSAT_DVB_T = 3 + FireSAT_DVB_T = 3, + FireSAT_DVB_S2 = 4 }; struct firesat { diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c index c7ccf633c24b..dcac70a2991e 100644 --- a/drivers/media/dvb/firesat/firesat_1394.c +++ b/drivers/media/dvb/firesat/firesat_1394.c @@ -263,6 +263,8 @@ static int firesat_probe(struct device *dev) int result; unsigned char subunitcount = 0xff, subunit; struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL); + int kv_len; + char *kv_buf; if (!firesats) { printk("%s: couldn't allocate memory.\n", __func__); @@ -329,6 +331,32 @@ static int firesat_probe(struct device *dev) firesat->subunit = subunit; + /* Reading device model from ROM */ + kv_len = (ud->model_name_kv->value.leaf.len - 2) * + sizeof(quadlet_t); + kv_buf = kmalloc((sizeof(quadlet_t) * kv_len), GFP_KERNEL); + memcpy(kv_buf, + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv), + kv_len); + while ((kv_buf + kv_len - 1) == '\0') kv_len--; + kv_buf[kv_len++] = '\0'; + + /* Determining the device model */ + if (strcmp(kv_buf, "FireDTV S/CI") == 0) { + printk(KERN_INFO "%s: found DVB/S\n", __func__); + firesat->type = 1; + } else if (strcmp(kv_buf, "FireDTV C/CI") == 0) { + printk(KERN_INFO "%s: found DVB/C\n", __func__); + firesat->type = 2; + } else if (strcmp(kv_buf, "FireDTV T/CI") == 0) { + printk(KERN_INFO "%s: found DVB/T\n", __func__); + firesat->type = 3; + } else if (strcmp(kv_buf, "FireDTV S2 ") == 0) { + printk(KERN_INFO "%s: found DVB/S2\n", __func__); + firesat->type = 4; + } + kfree(kv_buf); + if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type, &firesat->has_ci)) { printk("%s: cannot identify subunit %d\n", __func__, subunit); spin_lock_irqsave(&firesat_list_lock, flags); -- cgit v1.2.3 From 2c22861459f094e899c034515a9bb92ac307ceae Mon Sep 17 00:00:00 2001 From: Ben Backx <ben@bbackx.com> Date: Sat, 9 Aug 2008 14:35:55 +0200 Subject: firesat: add DVB-S support for DVB-S2 devices ...so S2 owners now can at least watch DVB-S channels in linux. Signed-off-by: Ben Backx <ben@bbackx.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/avc_api.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c index 0ad6420e342f..cd79c806ecc7 100644 --- a/drivers/media/dvb/firesat/avc_api.c +++ b/drivers/media/dvb/firesat/avc_api.c @@ -240,6 +240,12 @@ static void AVCTuner_tuneQPSK(struct firesat *firesat, struct dvb_frontend_param else CmdFrm->operand[12] = (firesat->tone==SEC_TONE_ON)?1:0; // band + if (firesat->type == FireSAT_DVB_S2) { + CmdFrm->operand[13] = 0x1; + CmdFrm->operand[14] = 0xFF; + CmdFrm->operand[15] = 0xFF; + } + CmdFrm->length = 16; } -- cgit v1.2.3 From df4846c35247a0d038c5359d502cddd59d04bc40 Mon Sep 17 00:00:00 2001 From: Henrik Kurelid <henke@kurelid.se> Date: Fri, 1 Aug 2008 10:00:45 +0200 Subject: firesat: update isochronous interface, add CI support I have finally managed to get the CI support for the card working. The implementation is a bare minimum to get encrypted channels to work in kaffeine. It works fine with my T/CI card. Now and then I get an AVC timeout and have to retune a channel in order to get it to work. Once the CAM seemed to hang so I needed to remove and insert it again. I.e. there are a number of glitches. The latest version contains the following changes: - Implemented the new hpsb iso interface so that data can be received from the card - Reduced some timers for demux setup which caused scanning to timeout - Added possibility to unload driver - Added support for getting C/N ratio - Added two debug parameters to the driver; ca_debug and avc_comm_debug. - Added CI support that works for me in kaffeine - Started working on CI MMI support. It now supports: o Enter menu o Receiving MMI objects - Added support for 64-bit platforms - Corrected DVB-C modulations problems Signed-off-by: Henrik Kurelid <henrik@kurelid.se> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (rebased, whitespace) --- drivers/media/dvb/firesat/Makefile | 1 + drivers/media/dvb/firesat/avc_api.c | 770 +++++++++++++++++++++++++------ drivers/media/dvb/firesat/avc_api.h | 296 +++++++----- drivers/media/dvb/firesat/cmp.c | 40 +- drivers/media/dvb/firesat/firesat-ci.c | 342 ++++++++++++-- drivers/media/dvb/firesat/firesat.h | 174 ++++++- drivers/media/dvb/firesat/firesat_1394.c | 123 +---- drivers/media/dvb/firesat/firesat_dvb.c | 49 +- drivers/media/dvb/firesat/firesat_fe.c | 80 ++-- drivers/media/dvb/firesat/firesat_iso.c | 106 +++++ 10 files changed, 1487 insertions(+), 494 deletions(-) create mode 100644 drivers/media/dvb/firesat/firesat_iso.c (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/Makefile b/drivers/media/dvb/firesat/Makefile index fdf86870f1fd..be7701b817c9 100644 --- a/drivers/media/dvb/firesat/Makefile +++ b/drivers/media/dvb/firesat/Makefile @@ -1,6 +1,7 @@ firesat-objs := firesat_1394.o \ firesat_dvb.o \ firesat_fe.o \ + firesat_iso.o \ avc_api.o \ cmp.o \ firesat-rc.o \ diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c index cd79c806ecc7..273c7235dd90 100644 --- a/drivers/media/dvb/firesat/avc_api.c +++ b/drivers/media/dvb/firesat/avc_api.c @@ -3,6 +3,7 @@ * * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com> * Copyright (c) 2008 Ben Backx <ben@bbackx.com> + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -15,6 +16,7 @@ #include <nodemgr.h> #include <asm/byteorder.h> #include <linux/delay.h> +#include <linux/crc32.h> #include "avc_api.h" #include "firesat-rc.h" @@ -22,6 +24,10 @@ #define COMMAND_REGISTER 0xFFFFF0000B00ULL #define PCR_BASE_ADDRESS 0xFFFFF0000900ULL +static unsigned int avc_comm_debug = 0; +module_param(avc_comm_debug, int, 0644); +MODULE_PARM_DESC(avc_comm_debug, "debug logging of AV/C communication, default is 0 (no)"); + static int __AVCRegisterRemoteControl(struct firesat*firesat, int internal); /* Frees an allocated packet */ @@ -47,7 +53,124 @@ static int avc_down_timeout(atomic_t *done, int timeout) return ((i > 0) ? 0:1); } -static int __AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) { +static const char* get_ctype_string(__u8 ctype) +{ + switch(ctype) + { + case 0: + return "CONTROL"; + case 1: + return "STATUS"; + case 2: + return "SPECIFIC_INQUIRY"; + case 3: + return "NOTIFY"; + case 4: + return "GENERAL_INQUIRY"; + } + return "UNKNOWN"; +} + +static const char* get_resp_string(__u8 ctype) +{ + switch(ctype) + { + case 8: + return "NOT_IMPLEMENTED"; + case 9: + return "ACCEPTED"; + case 10: + return "REJECTED"; + case 11: + return "IN_TRANSITION"; + case 12: + return "IMPLEMENTED_STABLE"; + case 13: + return "CHANGED"; + case 15: + return "INTERIM"; + } + return "UNKNOWN"; +} + +static const char* get_subunit_address(__u8 subunit_id, __u8 subunit_type) +{ + if (subunit_id == 7 && subunit_type == 0x1F) + return "Unit"; + if (subunit_id == 0 && subunit_type == 0x05) + return "Tuner(0)"; + return "Unsupported"; +} + +static const char* get_opcode_string(__u8 opcode) +{ + switch(opcode) + { + case 0x02: + return "PlugInfo"; + case 0x08: + return "OpenDescriptor"; + case 0x09: + return "ReadDescriptor"; + case 0x18: + return "OutputPlugSignalFormat"; + case 0x31: + return "SubunitInfo"; + case 0x30: + return "UnitInfo"; + case 0xB2: + return "Power"; + case 0xC8: + return "DirectSelectInformationType"; + case 0xCB: + return "DirectSelectData"; + case 0x00: + return "Vendor"; + + } + return "Unknown"; +} + +static void log_command_frame(const AVCCmdFrm *CmdFrm) +{ + int k; + printk(KERN_INFO "AV/C Command Frame:\n"); + printk("CommandType=%s, Address=%s(0x%02X,0x%02X), opcode=%s(0x%02X), " + "length=%d\n", get_ctype_string(CmdFrm->ctype), + get_subunit_address(CmdFrm->suid, CmdFrm->sutyp), + CmdFrm->suid, CmdFrm->sutyp, get_opcode_string(CmdFrm->opcode), + CmdFrm->opcode, CmdFrm->length); + for(k = 0; k < CmdFrm->length - 3; k++) { + if (k % 5 != 0) + printk(", "); + else if (k != 0) + printk("\n"); + printk("operand[%d] = %02X", k, CmdFrm->operand[k]); + } + printk("\n"); +} + +static void log_response_frame(const AVCRspFrm *RspFrm) +{ + int k; + printk(KERN_INFO "AV/C Response Frame:\n"); + printk("Response=%s, Address=%s(0x%02X,0x%02X), opcode=%s(0x%02X), " + "length=%d\n", get_resp_string(RspFrm->resp), + get_subunit_address(RspFrm->suid, RspFrm->sutyp), + RspFrm->suid, RspFrm->sutyp, get_opcode_string(RspFrm->opcode), + RspFrm->opcode, RspFrm->length); + for(k = 0; k < RspFrm->length - 3; k++) { + if (k % 5 != 0) + printk(", "); + else if (k != 0) + printk("\n"); + printk("operand[%d] = %02X", k, RspFrm->operand[k]); + } + printk("\n"); +} + +static int __AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, + AVCRspFrm *RspFrm) { struct hpsb_packet *packet; struct node_entry *ne; @@ -58,39 +181,50 @@ static int __AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, AVCRspFr } /* need all input data */ - if(!firesat || !ne || !CmdFrm) + if(!firesat || !ne || !CmdFrm) { + printk("%s: missing input data!\n",__func__); return -EINVAL; + } -// printk(KERN_INFO "AVCWrite command %x\n",CmdFrm->opcode); - -// for(k=0;k<CmdFrm->length;k++) -// printk(KERN_INFO "CmdFrm[%d] = %08x\n", k, ((quadlet_t*)CmdFrm)[k]); - - packet=hpsb_make_writepacket(ne->host, ne->nodeid, COMMAND_REGISTER, - (quadlet_t*)CmdFrm, CmdFrm->length); - - hpsb_set_packet_complete_task(packet, (void (*)(void*))avc_free_packet, - packet); - - hpsb_node_fill_packet(ne, packet); + if (avc_comm_debug == 1) { + log_command_frame(CmdFrm); + } if(RspFrm) atomic_set(&firesat->avc_reply_received, 0); + packet=hpsb_make_writepacket(ne->host, ne->nodeid, + COMMAND_REGISTER, + (quadlet_t*)CmdFrm, + CmdFrm->length); + hpsb_set_packet_complete_task(packet, + (void (*)(void*))avc_free_packet, + packet); + hpsb_node_fill_packet(ne, packet); + if (hpsb_send_packet(packet) < 0) { avc_free_packet(packet); atomic_set(&firesat->avc_reply_received, 1); + printk("%s: send failed!\n",__func__); return -EIO; } if(RspFrm) { - if(avc_down_timeout(&firesat->avc_reply_received,HZ/2)) { - printk("%s: timeout waiting for avc response\n",__func__); + // AV/C specs say that answers should be send within + // 150 ms so let's time out after 200 ms + if(avc_down_timeout(&firesat->avc_reply_received, + HZ / 5)) { + printk("%s: timeout waiting for avc response\n", + __func__); atomic_set(&firesat->avc_reply_received, 1); - return -ETIMEDOUT; - } - - memcpy(RspFrm,firesat->respfrm,firesat->resp_length); + return -ETIMEDOUT; + } + memcpy(RspFrm, firesat->respfrm, + firesat->resp_length); + RspFrm->length = firesat->resp_length; + if (avc_comm_debug == 1) { + log_response_frame(RspFrm); + } } return 0; @@ -137,6 +271,7 @@ int AVCRecv(struct firesat *firesat, u8 *data, size_t length) { // remote control handling +#if 0 AVCRspFrm *RspFrm = (AVCRspFrm*)data; if(/*RspFrm->length >= 8 && ###*/ @@ -155,21 +290,21 @@ int AVCRecv(struct firesat *firesat, u8 *data, size_t length) { printk(KERN_INFO "%s: remote control result = %d\n",__func__, RspFrm->resp); return 0; } - +#endif if(atomic_read(&firesat->avc_reply_received) == 1) { printk("%s: received out-of-order AVC response, ignored\n",__func__); return -EINVAL; } // AVCRspFrm *resp=(AVCRspFrm *)data; // int k; -/* - printk(KERN_INFO "resp=0x%x\n",resp->resp); - printk(KERN_INFO "cts=0x%x\n",resp->cts); - printk(KERN_INFO "suid=0x%x\n",resp->suid); - printk(KERN_INFO "sutyp=0x%x\n",resp->sutyp); - printk(KERN_INFO "opcode=0x%x\n",resp->opcode); - printk(KERN_INFO "length=%d\n",resp->length); -*/ + +// printk(KERN_INFO "resp=0x%x\n",resp->resp); +// printk(KERN_INFO "cts=0x%x\n",resp->cts); +// printk(KERN_INFO "suid=0x%x\n",resp->suid); +// printk(KERN_INFO "sutyp=0x%x\n",resp->sutyp); +// printk(KERN_INFO "opcode=0x%x\n",resp->opcode); +// printk(KERN_INFO "length=%d\n",resp->length); + // for(k=0;k<2;k++) // printk(KERN_INFO "operand[%d]=%02x\n",k,resp->operand[k]); @@ -183,6 +318,7 @@ int AVCRecv(struct firesat *firesat, u8 *data, size_t length) { // tuning command for setting the relative LNB frequency (not supported by the AVC standard) static void AVCTuner_tuneQPSK(struct firesat *firesat, struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) { + memset(CmdFrm, 0, sizeof(AVCCmdFrm)); CmdFrm->cts = AVC; @@ -249,7 +385,7 @@ static void AVCTuner_tuneQPSK(struct firesat *firesat, struct dvb_frontend_param CmdFrm->length = 16; } -int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, BYTE *status) { +int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, __u8 *status) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; M_VALID_FLAGS flags; @@ -274,21 +410,15 @@ int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params flags.Bits_T.TransmissionMode = (params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO); flags.Bits_T.NetworkId = 0; } else { - flags.Bits.Modulation = 0; - if(firesat->type == FireSAT_DVB_S) { - flags.Bits.FEC_inner = 1; - } else if(firesat->type == FireSAT_DVB_C) { - flags.Bits.FEC_inner = 0; - } + flags.Bits.Modulation = + (params->u.qam.modulation != QAM_AUTO); + flags.Bits.FEC_inner = + (params->u.qam.fec_inner != FEC_AUTO); flags.Bits.FEC_outer = 0; flags.Bits.Symbol_Rate = 1; flags.Bits.Frequency = 1; flags.Bits.Orbital_Pos = 0; - if(firesat->type == FireSAT_DVB_S) { - flags.Bits.Polarisation = 1; - } else if(firesat->type == FireSAT_DVB_C) { - flags.Bits.Polarisation = 0; - } + flags.Bits.Polarisation = 0; flags.Bits.reserved_fields = 0; flags.Bits.reserved1 = 0; flags.Bits.Network_ID = 0; @@ -306,15 +436,18 @@ int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params CmdFrm.operand[1] = 0xD2; // subfunction replace CmdFrm.operand[2] = 0x20; // system id = DVB CmdFrm.operand[3] = 0x00; // antenna number - CmdFrm.operand[4] = (firesat->type == FireSAT_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length + // system_specific_multiplex selection_length + CmdFrm.operand[4] = (firesat->type == FireSAT_DVB_T)?0x0c:0x11; CmdFrm.operand[5] = flags.Valid_Word.ByteHi; // valid_flags [0] CmdFrm.operand[6] = flags.Valid_Word.ByteLo; // valid_flags [1] if(firesat->type == FireSAT_DVB_T) { CmdFrm.operand[7] = 0x0; CmdFrm.operand[8] = (params->frequency/10) >> 24; - CmdFrm.operand[9] = ((params->frequency/10) >> 16) & 0xFF; - CmdFrm.operand[10] = ((params->frequency/10) >> 8) & 0xFF; + CmdFrm.operand[9] = + ((params->frequency/10) >> 16) & 0xFF; + CmdFrm.operand[10] = + ((params->frequency/10) >> 8) & 0xFF; CmdFrm.operand[11] = (params->frequency/10) & 0xFF; switch(params->u.ofdm.bandwidth) { case BANDWIDTH_7_MHZ: @@ -416,28 +549,24 @@ int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params CmdFrm.operand[16] = 0x00; // network_ID[1] CmdFrm.operand[17] = 0x00; // Nr_of_dsd_sel_specs = 0 - > No PIDs are transmitted - CmdFrm.length = 20; + CmdFrm.length = 24; } else { CmdFrm.operand[7] = 0x00; - CmdFrm.operand[8] = (((firesat->voltage==SEC_VOLTAGE_18)?0:1)<<6); /* 0 = H, 1 = V */ + CmdFrm.operand[8] = 0x00; CmdFrm.operand[9] = 0x00; CmdFrm.operand[10] = 0x00; - if(firesat->type == FireSAT_DVB_S) { - /* ### relative frequency -> absolute frequency */ - CmdFrm.operand[11] = (((params->frequency/4) >> 16) & 0xFF) | (2 << 6); - CmdFrm.operand[12] = ((params->frequency/4) >> 8) & 0xFF; - CmdFrm.operand[13] = (params->frequency/4) & 0xFF; - } else if(firesat->type == FireSAT_DVB_C) { - CmdFrm.operand[11] = (((params->frequency/4000) >> 16) & 0xFF) | (2 << 6); - CmdFrm.operand[12] = ((params->frequency/4000) >> 8) & 0xFF; - CmdFrm.operand[13] = (params->frequency/4000) & 0xFF; - } - - CmdFrm.operand[14] = ((params->u.qpsk.symbol_rate/1000) >> 12) & 0xFF; - CmdFrm.operand[15] = ((params->u.qpsk.symbol_rate/1000) >> 4) & 0xFF; - CmdFrm.operand[16] = ((params->u.qpsk.symbol_rate/1000) << 4) & 0xF0; - + CmdFrm.operand[11] = + (((params->frequency/4000) >> 16) & 0xFF) | (2 << 6); + CmdFrm.operand[12] = + ((params->frequency/4000) >> 8) & 0xFF; + CmdFrm.operand[13] = (params->frequency/4000) & 0xFF; + CmdFrm.operand[14] = + ((params->u.qpsk.symbol_rate/1000) >> 12) & 0xFF; + CmdFrm.operand[15] = + ((params->u.qpsk.symbol_rate/1000) >> 4) & 0xFF; + CmdFrm.operand[16] = + ((params->u.qpsk.symbol_rate/1000) << 4) & 0xF0; CmdFrm.operand[17] = 0x00; switch(params->u.qpsk.fec_inner) { case FEC_1_2: @@ -455,35 +584,35 @@ int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params case FEC_7_8: CmdFrm.operand[18] = 0x5; break; - case FEC_4_5: case FEC_8_9: + CmdFrm.operand[18] = 0x6; + break; + case FEC_4_5: + CmdFrm.operand[18] = 0x8; + break; case FEC_AUTO: default: CmdFrm.operand[18] = 0x0; } - if(firesat->type == FireSAT_DVB_S) { + switch(params->u.qam.modulation) { + case QAM_16: CmdFrm.operand[19] = 0x08; // modulation - } else if(firesat->type == FireSAT_DVB_C) { - switch(params->u.qam.modulation) { - case QAM_16: - CmdFrm.operand[19] = 0x08; // modulation - break; - case QAM_32: - CmdFrm.operand[19] = 0x10; // modulation - break; - case QAM_64: - CmdFrm.operand[19] = 0x18; // modulation - break; - case QAM_128: - CmdFrm.operand[19] = 0x20; // modulation - break; - case QAM_256: - CmdFrm.operand[19] = 0x28; // modulation - break; - case QAM_AUTO: - default: - CmdFrm.operand[19] = 0x00; // modulation - } + break; + case QAM_32: + CmdFrm.operand[19] = 0x10; // modulation + break; + case QAM_64: + CmdFrm.operand[19] = 0x18; // modulation + break; + case QAM_128: + CmdFrm.operand[19] = 0x20; // modulation + break; + case QAM_256: + CmdFrm.operand[19] = 0x28; // modulation + break; + case QAM_AUTO: + default: + CmdFrm.operand[19] = 0x00; // modulation } CmdFrm.operand[20] = 0x00; CmdFrm.operand[21] = 0x00; @@ -496,7 +625,6 @@ int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params if((k=AVCWrite(firesat,&CmdFrm,&RspFrm))) return k; -// msleep(250); mdelay(500); if(status) @@ -504,13 +632,12 @@ int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params return 0; } -int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) { +int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) +{ AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; int pos,k; - printk(KERN_INFO "%s\n", __func__); - if(pidc > 16 && pidc != 0xFF) return -EINVAL; @@ -526,49 +653,11 @@ int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) { CmdFrm.operand[1] = 0xD2; // subfunction replace CmdFrm.operand[2] = 0x20; // system id = DVB CmdFrm.operand[3] = 0x00; // antenna number - CmdFrm.operand[4] = 0x11; // system_specific_multiplex selection_length - CmdFrm.operand[5] = 0x00; // valid_flags [0] - CmdFrm.operand[6] = 0x00; // valid_flags [1] - - if(firesat->type == FireSAT_DVB_T) { -/* CmdFrm.operand[7] = 0x00; - CmdFrm.operand[8] = 0x00;//(params->frequency/10) >> 24; - CmdFrm.operand[9] = 0x00;//((params->frequency/10) >> 16) & 0xFF; - CmdFrm.operand[10] = 0x00;//((params->frequency/10) >> 8) & 0xFF; - CmdFrm.operand[11] = 0x00;//(params->frequency/10) & 0xFF; - CmdFrm.operand[12] = 0x00; - CmdFrm.operand[13] = 0x00; - CmdFrm.operand[14] = 0x00; - - CmdFrm.operand[15] = 0x00; // network_ID[0] - CmdFrm.operand[16] = 0x00; // network_ID[1] -*/ CmdFrm.operand[17] = pidc; // Nr_of_dsd_sel_specs - - pos=18; - } else { -/* CmdFrm.operand[7] = 0x00; - CmdFrm.operand[8] = 0x00; - CmdFrm.operand[9] = 0x00; - CmdFrm.operand[10] = 0x00; - - CmdFrm.operand[11] = 0x00;//(((params->frequency/4) >> 16) & 0xFF) | (2 << 6); - CmdFrm.operand[12] = 0x00;//((params->frequency/4) >> 8) & 0xFF; - CmdFrm.operand[13] = 0x00;//(params->frequency/4) & 0xFF; - - CmdFrm.operand[14] = 0x00;//((params->u.qpsk.symbol_rate/1000) >> 12) & 0xFF; - CmdFrm.operand[15] = 0x00;//((params->u.qpsk.symbol_rate/1000) >> 4) & 0xFF; - CmdFrm.operand[16] = 0x00;//((params->u.qpsk.symbol_rate/1000) << 4) & 0xF0; - - CmdFrm.operand[17] = 0x00; - CmdFrm.operand[18] = 0x00; - CmdFrm.operand[19] = 0x00; // modulation - CmdFrm.operand[20] = 0x00; - CmdFrm.operand[21] = 0x00;*/ - CmdFrm.operand[22] = pidc; // Nr_of_dsd_sel_specs - - pos=23; - } - if(pidc != 0xFF) + CmdFrm.operand[4] = 0x00; // system_specific_multiplex selection_length + CmdFrm.operand[5] = pidc; // Nr_of_dsd_sel_specs + + pos=6; + if(pidc != 0xFF) { for(k=0;k<pidc;k++) { CmdFrm.operand[pos++] = 0x13; // flowfunction relay CmdFrm.operand[pos++] = 0x80; // dsd_sel_spec_valid_flags -> PID @@ -577,17 +666,16 @@ int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) { CmdFrm.operand[pos++] = 0x00; // tableID CmdFrm.operand[pos++] = 0x00; // filter_length } + } CmdFrm.length = pos+3; - if((pos+3)%4) CmdFrm.length += 4 - ((pos+3)%4); if((k=AVCWrite(firesat,&CmdFrm,&RspFrm))) return k; - mdelay(250); - + mdelay(50); return 0; } @@ -596,7 +684,7 @@ int AVCTuner_GetTS(struct firesat *firesat){ AVCRspFrm RspFrm; int k; - printk(KERN_INFO "%s\n", __func__); + //printk(KERN_INFO "%s\n", __func__); memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); @@ -612,20 +700,21 @@ int AVCTuner_GetTS(struct firesat *firesat){ CmdFrm.operand[3] = 0x20; // system id = DVB CmdFrm.operand[4] = 0x00; // antenna number CmdFrm.operand[5] = 0x0; // system_specific_search_flags - CmdFrm.operand[6] = 0x11; // system_specific_multiplex selection_length + CmdFrm.operand[6] = (firesat->type == FireSAT_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length CmdFrm.operand[7] = 0x00; // valid_flags [0] CmdFrm.operand[8] = 0x00; // valid_flags [1] - CmdFrm.operand[24] = 0x00; // nr_of_dsit_sel_specs (always 0) + CmdFrm.operand[7 + (firesat->type == FireSAT_DVB_T)?0x0c:0x11] = 0x00; // nr_of_dsit_sel_specs (always 0) - CmdFrm.length = 28; + CmdFrm.length = (firesat->type == FireSAT_DVB_T)?24:28; - if((k=AVCWrite(firesat, &CmdFrm, &RspFrm))) return k; + if ((k=AVCWrite(firesat, &CmdFrm, &RspFrm))) + return k; mdelay(250); return 0; } -int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport, int *has_ci) { +int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; @@ -660,8 +749,6 @@ int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *tr } if(systemId) *systemId = RspFrm.operand[7]; - if(has_ci) - *has_ci = (RspFrm.operand[14] >> 4) & 0x1; return 0; } @@ -679,14 +766,13 @@ int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_in CmdFrm.opcode=READ_DESCRIPTOR; CmdFrm.operand[0]=DESCRIPTOR_TUNER_STATUS; - CmdFrm.operand[1]=0xff; - CmdFrm.operand[2]=0x00; - CmdFrm.operand[3]=sizeof(ANTENNA_INPUT_INFO) >> 8; - CmdFrm.operand[4]=sizeof(ANTENNA_INPUT_INFO) & 0xFF; + CmdFrm.operand[1]=0xff; //read_result_status + CmdFrm.operand[2]=0x00; // reserver + CmdFrm.operand[3]=0;//sizeof(ANTENNA_INPUT_INFO) >> 8; + CmdFrm.operand[4]=0;//sizeof(ANTENNA_INPUT_INFO) & 0xFF; CmdFrm.operand[5]=0x00; - CmdFrm.operand[6]=0x03; + CmdFrm.operand[6]=0x00; CmdFrm.length=12; - //Absenden des AVC request und warten auf response if (AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) return -EIO; @@ -695,10 +781,11 @@ int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_in return -EINVAL; } - length = (RspFrm.operand[3] << 8) + RspFrm.operand[4]; - if(length == sizeof(ANTENNA_INPUT_INFO)) + length = RspFrm.operand[9]; + if(RspFrm.operand[1] == 0x10 && length == sizeof(ANTENNA_INPUT_INFO)) { - memcpy(antenna_input_info,&RspFrm.operand[7],length); + memcpy(antenna_input_info, &RspFrm.operand[10], + sizeof(ANTENNA_INPUT_INFO)); return 0; } printk("%s: invalid info returned from AVC\n",__func__); @@ -837,3 +924,384 @@ int AVCRegisterRemoteControl(struct firesat*firesat) { return __AVCRegisterRemoteControl(firesat, 0); } + +int AVCTuner_Host2Ca(struct firesat *firesat) +{ + + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + return 0; +} + +static int get_ca_object_pos(AVCRspFrm *RspFrm) +{ + int length = 1; + + // Check length of length field + if (RspFrm->operand[7] & 0x80) + length = (RspFrm->operand[7] & 0x7F) + 1; + return length + 7; +} + +static int get_ca_object_length(AVCRspFrm *RspFrm) +{ + int size = 0; + int i; + + if (RspFrm->operand[7] & 0x80) { + for (i = 0; i < (RspFrm->operand[7] & 0x7F); i++) { + size <<= 8; + size += RspFrm->operand[8 + i]; + } + } + return RspFrm->operand[7]; +} + +int avc_ca_app_info(struct firesat *firesat, char *app_info, int *length) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int pos; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag + CmdFrm.length = 12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + + pos = get_ca_object_pos(&RspFrm); + app_info[0] = (TAG_APP_INFO >> 16) & 0xFF; + app_info[1] = (TAG_APP_INFO >> 8) & 0xFF; + app_info[2] = (TAG_APP_INFO >> 0) & 0xFF; + app_info[3] = 6 + RspFrm.operand[pos + 4]; + app_info[4] = 0x01; + memcpy(&app_info[5], &RspFrm.operand[pos], 5 + RspFrm.operand[pos + 4]); + *length = app_info[3] + 4; + + return 0; +} + +int avc_ca_info(struct firesat *firesat, char *app_info, int *length) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int pos; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag + CmdFrm.length = 12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + pos = get_ca_object_pos(&RspFrm); + app_info[0] = (TAG_CA_INFO >> 16) & 0xFF; + app_info[1] = (TAG_CA_INFO >> 8) & 0xFF; + app_info[2] = (TAG_CA_INFO >> 0) & 0xFF; + app_info[3] = 2; + app_info[4] = app_info[5]; + app_info[5] = app_info[6]; + *length = app_info[3] + 4; + + return 0; +} + +int avc_ca_reset(struct firesat *firesat) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_RESET; // ca tag + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 1; // length + CmdFrm.operand[8] = 0; // force hardware reset + CmdFrm.length = 12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + return 0; +} + +int avc_ca_pmt(struct firesat *firesat, char *msg, int length) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int list_management; + int program_info_length; + int pmt_cmd_id; + int read_pos; + int write_pos; + int es_info_length; + int crc32_csum; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + if (msg[0] != LIST_MANAGEMENT_ONLY) { + printk(KERN_ERR "The only list_manasgement parameter that is " + "supported by the firesat driver is \"only\" (3)."); + return -EFAULT; + } + // We take the cmd_id from the programme level only! + list_management = msg[0]; + program_info_length = ((msg[4] & 0x0F) << 8) + msg[5]; + if (program_info_length > 0) + program_info_length--; // Remove pmt_cmd_id + pmt_cmd_id = msg[6]; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_PMT; // ca tag + CmdFrm.operand[6] = 0; // more/last + //CmdFrm.operand[7] = XXXprogram_info_length + 17; // length + CmdFrm.operand[8] = list_management; + CmdFrm.operand[9] = 0x01; // pmt_cmd=OK_descramble + + // TS program map table + + // Table id=2 + CmdFrm.operand[10] = 0x02; + // Section syntax + length + CmdFrm.operand[11] = 0x80; + //CmdFrm.operand[12] = XXXprogram_info_length + 12; + // Program number + CmdFrm.operand[13] = msg[1]; + CmdFrm.operand[14] = msg[2]; + // Version number=0 + current/next=1 + CmdFrm.operand[15] = 0x01; + // Section number=0 + CmdFrm.operand[16] = 0x00; + // Last section number=0 + CmdFrm.operand[17] = 0x00; + // PCR_PID=1FFF + CmdFrm.operand[18] = 0x1F; + CmdFrm.operand[19] = 0xFF; + // Program info length + CmdFrm.operand[20] = (program_info_length >> 8); + CmdFrm.operand[21] = (program_info_length & 0xFF); + // CA descriptors at programme level + read_pos = 6; + write_pos = 22; + if (program_info_length > 0) { +/* printk(KERN_INFO "Copying descriptors at programme level.\n"); */ + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id !=4) { + printk(KERN_ERR "Invalid pmt_cmd_id=%d.\n", + pmt_cmd_id); + } + memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], + program_info_length); + read_pos += program_info_length; + write_pos += program_info_length; + } + while (read_pos < length) { +/* printk(KERN_INFO "Copying descriptors at stream level for " */ +/* "stream type %d.\n", msg[read_pos]); */ + CmdFrm.operand[write_pos++] = msg[read_pos++]; + CmdFrm.operand[write_pos++] = msg[read_pos++]; + CmdFrm.operand[write_pos++] = msg[read_pos++]; + es_info_length = + ((msg[read_pos] & 0x0F) << 8) + msg[read_pos + 1]; + read_pos += 2; + if (es_info_length > 0) + es_info_length--; // Remove pmt_cmd_id + CmdFrm.operand[write_pos++] = es_info_length >> 8; + CmdFrm.operand[write_pos++] = es_info_length & 0xFF; + if (es_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id !=4) { + printk(KERN_ERR "Invalid pmt_cmd_id=%d at " + "stream level.\n", pmt_cmd_id); + } + memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], + es_info_length); + read_pos += es_info_length; + write_pos += es_info_length; + } + } + + // CRC + CmdFrm.operand[write_pos++] = 0x00; + CmdFrm.operand[write_pos++] = 0x00; + CmdFrm.operand[write_pos++] = 0x00; + CmdFrm.operand[write_pos++] = 0x00; + + CmdFrm.operand[7] = write_pos - 8; + CmdFrm.operand[12] = write_pos - 13; + + crc32_csum = crc32_be(0, &CmdFrm.operand[10], + CmdFrm.operand[12] - 1); + CmdFrm.operand[write_pos - 4] = (crc32_csum >> 24) & 0xFF; + CmdFrm.operand[write_pos - 3] = (crc32_csum >> 16) & 0xFF; + CmdFrm.operand[write_pos - 2] = (crc32_csum >> 8) & 0xFF; + CmdFrm.operand[write_pos - 1] = (crc32_csum >> 0) & 0xFF; + + CmdFrm.length = write_pos + 3; + if ((write_pos + 3) % 4) + CmdFrm.length += 4 - ((write_pos + 3) % 4); + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + if (RspFrm.resp != ACCEPTED) { + printk(KERN_ERR "Answer to CA PMT was %d\n", RspFrm.resp); + return -EFAULT; + } + + return 0; + +} + +int avc_ca_get_time_date(struct firesat *firesat, int *interval) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; // ca tag + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + *interval = RspFrm.operand[get_ca_object_pos(&RspFrm)]; + + return 0; +} + +int avc_ca_enter_menu(struct firesat *firesat) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + return 0; +} + +int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, int *length) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_MMI; + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + return -EIO; + + *length = get_ca_object_length(&RspFrm); + memcpy(mmi_object, &RspFrm.operand[get_ca_object_pos(&RspFrm)], *length); + + return 0; +} diff --git a/drivers/media/dvb/firesat/avc_api.h b/drivers/media/dvb/firesat/avc_api.h index f9a190adcd37..041665685903 100644 --- a/drivers/media/dvb/firesat/avc_api.h +++ b/drivers/media/dvb/firesat/avc_api.h @@ -4,6 +4,7 @@ begin : Wed May 1 2000 copyright : (C) 2000 by Manfred Weihs copyright : (C) 2003 by Philipp Gutgsell + copyright : (C) 2008 by Henrik Kurelid (henrik@kurelid.se) email : 0014guph@edu.fh-kaernten.ac.at ***************************************************************************/ @@ -27,12 +28,10 @@ #include <linux/dvb/frontend.h> -#define BYTE unsigned char -#define WORD unsigned short -#define DWORD unsigned long -#define ULONG unsigned long -#define LONG long - +/************************************************************* + Constants from EN510221 +**************************************************************/ +#define LIST_MANAGEMENT_ONLY 0x03 /************************************************************* FCP Address range @@ -68,12 +67,12 @@ typedef struct { typedef struct _AVCCmdFrm { // AV/C command frame - BYTE ctype : 4 ; // command type - BYTE cts : 4 ; // always 0x0 for AVC - BYTE suid : 3 ; // subunit ID - BYTE sutyp : 5 ; // subunit_typ - BYTE opcode : 8 ; // opcode - BYTE operand[509] ; // array of operands [1-507] + __u8 ctype : 4 ; // command type + __u8 cts : 4 ; // always 0x0 for AVC + __u8 suid : 3 ; // subunit ID + __u8 sutyp : 5 ; // subunit_typ + __u8 opcode : 8 ; // opcode + __u8 operand[509] ; // array of operands [1-507] int length; //length of the command frame } AVCCmdFrm ; @@ -81,12 +80,12 @@ typedef struct _AVCCmdFrm typedef struct _AVCRspFrm { // AV/C response frame - BYTE resp : 4 ; // response type - BYTE cts : 4 ; // always 0x0 for AVC - BYTE suid : 3 ; // subunit ID - BYTE sutyp : 5 ; // subunit_typ - BYTE opcode : 8 ; // opcode - BYTE operand[509] ; // array of operands [1-507] + __u8 resp : 4 ; // response type + __u8 cts : 4 ; // always 0x0 for AVC + __u8 suid : 3 ; // subunit ID + __u8 sutyp : 5 ; // subunit_typ + __u8 opcode : 8 ; // opcode + __u8 operand[509] ; // array of operands [1-507] int length; //length of the response frame } AVCRspFrm ; @@ -94,23 +93,23 @@ typedef struct _AVCRspFrm typedef struct _AVCCmdFrm { - BYTE cts:4; - BYTE ctype:4; - BYTE sutyp:5; - BYTE suid:3; - BYTE opcode; - BYTE operand[509]; + __u8 cts:4; + __u8 ctype:4; + __u8 sutyp:5; + __u8 suid:3; + __u8 opcode; + __u8 operand[509]; int length; } AVCCmdFrm; typedef struct _AVCRspFrm { - BYTE cts:4; - BYTE resp:4; - BYTE sutyp:5; - BYTE suid:3; - BYTE opcode; - BYTE operand[509]; + __u8 cts:4; + __u8 resp:4; + __u8 sutyp:5; + __u8 suid:3; + __u8 opcode; + __u8 operand[509]; int length; } AVCRspFrm; @@ -197,6 +196,14 @@ typedef struct _AVCRspFrm #define SFE_VENDOR_OPCODE_CISTATUS 0x59 #define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 // QPSK command for DVB-S2 devices +// CA Tags +#define SFE_VENDOR_TAG_CA_RESET 0x00 +#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 +#define SFE_VENDOR_TAG_CA_PMT 0x02 +#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 +#define SFE_VENDOR_TAG_CA_MMI 0x05 +#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 + //AVCTuner DVB identifier service_ID #define DVB 0x20 @@ -209,8 +216,8 @@ typedef struct _AVCRspFrm #define Tuner_Status_Descriptor 0x80 typedef struct { - BYTE Subunit_Type; - BYTE Max_Subunit_ID; + __u8 Subunit_Type; + __u8 Max_Subunit_ID; } SUBUNIT_INFO; /************************************************************* @@ -220,12 +227,12 @@ typedef struct { **************************************************************/ typedef struct { - BYTE Byte0; - BYTE Byte1; - BYTE Byte2; - BYTE Byte3; - BYTE Byte4; - BYTE Byte5; + __u8 Byte0; + __u8 Byte1; + __u8 Byte2; + __u8 Byte3; + __u8 Byte4; + __u8 Byte5; }OBJECT_ID; /************************************************************* @@ -234,14 +241,14 @@ typedef struct { typedef struct { #ifdef __LITTLE_ENDIAN - BYTE RF_frequency_hByte:6; - BYTE raster_Frequency:2;//Bit7,6 raster frequency + __u8 RF_frequency_hByte:6; + __u8 raster_Frequency:2;//Bit7,6 raster frequency #else - BYTE raster_Frequency:2; - BYTE RF_frequency_hByte:6; + __u8 raster_Frequency:2; + __u8 RF_frequency_hByte:6; #endif - BYTE RF_frequency_mByte; - BYTE RF_frequency_lByte; + __u8 RF_frequency_mByte; + __u8 RF_frequency_lByte; }FREQUENCY; @@ -249,63 +256,63 @@ typedef struct typedef struct { - BYTE Modulation :1; - BYTE FEC_inner :1; - BYTE FEC_outer :1; - BYTE Symbol_Rate :1; - BYTE Frequency :1; - BYTE Orbital_Pos :1; - BYTE Polarisation :1; - BYTE reserved_fields :1; - BYTE reserved1 :7; - BYTE Network_ID :1; + __u8 Modulation :1; + __u8 FEC_inner :1; + __u8 FEC_outer :1; + __u8 Symbol_Rate :1; + __u8 Frequency :1; + __u8 Orbital_Pos :1; + __u8 Polarisation :1; + __u8 reserved_fields :1; + __u8 reserved1 :7; + __u8 Network_ID :1; }MULTIPLEX_VALID_FLAGS; typedef struct { - BYTE GuardInterval:1; - BYTE CodeRateLPStream:1; - BYTE CodeRateHPStream:1; - BYTE HierarchyInfo:1; - BYTE Constellation:1; - BYTE Bandwidth:1; - BYTE CenterFrequency:1; - BYTE reserved1:1; - BYTE reserved2:5; - BYTE OtherFrequencyFlag:1; - BYTE TransmissionMode:1; - BYTE NetworkId:1; + __u8 GuardInterval:1; + __u8 CodeRateLPStream:1; + __u8 CodeRateHPStream:1; + __u8 HierarchyInfo:1; + __u8 Constellation:1; + __u8 Bandwidth:1; + __u8 CenterFrequency:1; + __u8 reserved1:1; + __u8 reserved2:5; + __u8 OtherFrequencyFlag:1; + __u8 TransmissionMode:1; + __u8 NetworkId:1; }MULTIPLEX_VALID_FLAGS_DVBT; #else typedef struct { - BYTE reserved_fields:1; - BYTE Polarisation:1; - BYTE Orbital_Pos:1; - BYTE Frequency:1; - BYTE Symbol_Rate:1; - BYTE FEC_outer:1; - BYTE FEC_inner:1; - BYTE Modulation:1; - BYTE Network_ID:1; - BYTE reserved1:7; + __u8 reserved_fields:1; + __u8 Polarisation:1; + __u8 Orbital_Pos:1; + __u8 Frequency:1; + __u8 Symbol_Rate:1; + __u8 FEC_outer:1; + __u8 FEC_inner:1; + __u8 Modulation:1; + __u8 Network_ID:1; + __u8 reserved1:7; }MULTIPLEX_VALID_FLAGS; typedef struct { - BYTE reserved1:1; - BYTE CenterFrequency:1; - BYTE Bandwidth:1; - BYTE Constellation:1; - BYTE HierarchyInfo:1; - BYTE CodeRateHPStream:1; - BYTE CodeRateLPStream:1; - BYTE GuardInterval:1; - BYTE NetworkId:1; - BYTE TransmissionMode:1; - BYTE OtherFrequencyFlag:1; - BYTE reserved2:5; + __u8 reserved1:1; + __u8 CenterFrequency:1; + __u8 Bandwidth:1; + __u8 Constellation:1; + __u8 HierarchyInfo:1; + __u8 CodeRateHPStream:1; + __u8 CodeRateLPStream:1; + __u8 GuardInterval:1; + __u8 NetworkId:1; + __u8 TransmissionMode:1; + __u8 OtherFrequencyFlag:1; + __u8 reserved2:5; }MULTIPLEX_VALID_FLAGS_DVBT; #endif @@ -314,47 +321,98 @@ typedef union { MULTIPLEX_VALID_FLAGS Bits; MULTIPLEX_VALID_FLAGS_DVBT Bits_T; struct { - BYTE ByteHi; - BYTE ByteLo; + __u8 ByteHi; + __u8 ByteLo; } Valid_Word; } M_VALID_FLAGS; typedef struct { #ifdef __LITTLE_ENDIAN - BYTE ActiveSystem; - BYTE reserved:5; - BYTE NoRF:1; - BYTE Moving:1; - BYTE Searching:1; + __u8 ActiveSystem; + __u8 reserved:5; + __u8 NoRF:1; + __u8 Moving:1; + __u8 Searching:1; - BYTE SelectedAntenna:7; - BYTE Input:1; + __u8 SelectedAntenna:7; + __u8 Input:1; - BYTE BER[4]; + __u8 BER[4]; - BYTE SignalStrength; + __u8 SignalStrength; FREQUENCY Frequency; - BYTE ManDepInfoLength; + __u8 ManDepInfoLength; + + __u8 PowerSupply:1; + __u8 FrontEndPowerStatus:1; + __u8 reserved3:1; + __u8 AntennaError:1; + __u8 FrontEndError:1; + __u8 reserved2:3; + + __u8 CarrierNoiseRatio[2]; + __u8 reserved4[2]; + __u8 PowerSupplyVoltage; + __u8 AntennaVoltage; + __u8 FirewireBusVoltage; + + __u8 CaMmi:1; + __u8 reserved5:7; + + __u8 reserved6:1; + __u8 CaInitializationStatus:1; + __u8 CaErrorFlag:1; + __u8 CaDvbFlag:1; + __u8 CaModulePresentStatus:1; + __u8 CaApplicationInfo:1; + __u8 CaDateTimeRequest:1; + __u8 CaPmtReply:1; + #else - BYTE ActiveSystem; - BYTE Searching:1; - BYTE Moving:1; - BYTE NoRF:1; - BYTE reserved:5; + __u8 ActiveSystem; + __u8 Searching:1; + __u8 Moving:1; + __u8 NoRF:1; + __u8 reserved:5; - BYTE Input:1; - BYTE SelectedAntenna:7; + __u8 Input:1; + __u8 SelectedAntenna:7; - BYTE BER[4]; + __u8 BER[4]; - BYTE SignalStrength; + __u8 SignalStrength; FREQUENCY Frequency; - BYTE ManDepInfoLength; + __u8 ManDepInfoLength; + + __u8 reserved2:3; + __u8 FrontEndError:1; + __u8 AntennaError:1; + __u8 reserved3:1; + __u8 FrontEndPowerStatus:1; + __u8 PowerSupply:1; + + __u8 CarrierNoiseRatio[2]; + __u8 reserved4[2]; + __u8 PowerSupplyVoltage; + __u8 AntennaVoltage; + __u8 FirewireBusVoltage; + + __u8 reserved5:7; + __u8 CaMmi:1; + __u8 CaPmtReply:1; + __u8 CaDateTimeRequest:1; + __u8 CaApplicationInfo:1; + __u8 CaModulePresentStatus:1; + __u8 CaDvbFlag:1; + __u8 CaErrorFlag:1; + __u8 CaInitializationStatus:1; + __u8 reserved6:1; + #endif -} ANTENNA_INPUT_INFO; // 11 Byte +} ANTENNA_INPUT_INFO; // 22 Byte #define LNBCONTROL_DONTCARE 0xff @@ -365,17 +423,27 @@ extern int AVCRecv(struct firesat *firesat, u8 *data, size_t length); extern int AVCTuner_DSIT(struct firesat *firesat, int Source_Plug, struct dvb_frontend_parameters *params, - BYTE *status); + __u8 *status); extern int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_info); -extern int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, BYTE *status); +extern int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, __u8 *status); extern int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]); extern int AVCTuner_GetTS(struct firesat *firesat); -extern int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport, int *has_ci); +extern int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport); extern int AVCLNBControl(struct firesat *firesat, char voltage, char burst, char conttone, char nrdiseq, struct dvb_diseqc_master_cmd *diseqcmd); extern int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount); extern int AVCRegisterRemoteControl(struct firesat *firesat); +extern int AVCTuner_Host2Ca(struct firesat *firesat); +extern int avc_ca_app_info(struct firesat *firesat, char *app_info, + int *length); +extern int avc_ca_info(struct firesat *firesat, char *app_info, int *length); +extern int avc_ca_reset(struct firesat *firesat); +extern int avc_ca_pmt(struct firesat *firesat, char *app_info, int length); +extern int avc_ca_get_time_date(struct firesat *firesat, int *interval); +extern int avc_ca_enter_menu(struct firesat *firesat); +extern int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, + int *length); #endif diff --git a/drivers/media/dvb/firesat/cmp.c b/drivers/media/dvb/firesat/cmp.c index 37b91f3f7ff1..a1291caa0674 100644 --- a/drivers/media/dvb/firesat/cmp.c +++ b/drivers/media/dvb/firesat/cmp.c @@ -1,3 +1,15 @@ +/* + * FireSAT DVB driver + * + * Copyright (c) ? + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + #include "cmp.h" #include <ieee1394.h> #include <nodemgr.h> @@ -10,18 +22,18 @@ typedef struct _OPCR { - BYTE PTPConnCount : 6 ; // Point to point connect. counter - BYTE BrConnCount : 1 ; // Broadcast connection counter - BYTE OnLine : 1 ; // On Line + __u8 PTPConnCount : 6 ; // Point to point connect. counter + __u8 BrConnCount : 1 ; // Broadcast connection counter + __u8 OnLine : 1 ; // On Line - BYTE ChNr : 6 ; // Channel number - BYTE Res : 2 ; // Reserved + __u8 ChNr : 6 ; // Channel number + __u8 Res : 2 ; // Reserved - BYTE PayloadHi : 2 ; // Payoad high bits - BYTE OvhdID : 4 ; // Overhead ID - BYTE DataRate : 2 ; // Data Rate + __u8 PayloadHi : 2 ; // Payoad high bits + __u8 OvhdID : 4 ; // Overhead ID + __u8 DataRate : 2 ; // Data Rate - BYTE PayloadLo ; // Payoad low byte + __u8 PayloadLo ; // Payoad low byte } OPCR ; #define FIRESAT_SPEED IEEE1394_SPEED_400 @@ -94,13 +106,13 @@ int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, int i u64 oPCR_address=0xfffff0000904ull+(output_plug << 2); int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4); - printk(KERN_INFO "%s: nodeid = %d\n",__func__,firesat->nodeentry->nodeid); +/* printk(KERN_INFO "%s: nodeid = %d\n",__func__,firesat->nodeentry->nodeid); */ if (result < 0) { printk("%s: cannot read oPCR\n", __func__); return result; } else { - printk(KERN_INFO "%s: oPCR = %08x\n",__func__,test_oPCR); +/* printk(KERN_INFO "%s: oPCR = %08x\n",__func__,test_oPCR); */ do { OPCR *hilf= (OPCR*) &test_oPCR; @@ -134,8 +146,8 @@ int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, int i hilf->PTPConnCount++; new_oPCR=test_oPCR; - printk(KERN_INFO "%s: trying compare_swap...\n",__func__); - printk(KERN_INFO "%s: oPCR_old: %08x, oPCR_new: %08x\n",__func__, old_oPCR, new_oPCR); +/* printk(KERN_INFO "%s: trying compare_swap...\n",__func__); */ +/* printk(KERN_INFO "%s: oPCR_old: %08x, oPCR_new: %08x\n",__func__, old_oPCR, new_oPCR); */ result=cmp_lock(firesat, &test_oPCR, oPCR_address, old_oPCR, 2); if (result < 0) { @@ -169,7 +181,7 @@ int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug,int iso_ch u64 oPCR_address=0xfffff0000904ull+(output_plug << 2); int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4); - printk(KERN_INFO "%s\n",__func__); +/* printk(KERN_INFO "%s\n",__func__); */ if (result < 0) { printk("%s: cannot read oPCR\n", __func__); diff --git a/drivers/media/dvb/firesat/firesat-ci.c b/drivers/media/dvb/firesat/firesat-ci.c index 862d9553c5bc..821048db283b 100644 --- a/drivers/media/dvb/firesat/firesat-ci.c +++ b/drivers/media/dvb/firesat/firesat-ci.c @@ -1,66 +1,303 @@ +/* + * FireSAT DVB driver + * + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + #include "firesat-ci.h" #include "firesat.h" #include "avc_api.h" #include <linux/dvb/ca.h> #include <dvbdev.h> -/* -static int firesat_ca_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { - //struct firesat *firesat = (struct firesat*)((struct dvb_device*)file->private_data)->priv; - int err; -// printk(KERN_INFO "%s: ioctl %d\n",__func__,cmd); +static unsigned int ca_debug = 0; +module_param(ca_debug, int, 0644); +MODULE_PARM_DESC(ca_debug, "debug logging of ca system, default is 0 (no)"); - switch(cmd) { - case CA_RESET: - // TODO: Needs to be implemented with new AVC Vendor commands +static int firesat_ca_ready(ANTENNA_INPUT_INFO *info) +{ + if (ca_debug != 0) + printk("%s: CaMmi=%d, CaInit=%d, CaError=%d, CaDvb=%d, " + "CaModule=%d, CaAppInfo=%d, CaDateTime=%d, " + "CaPmt=%d\n", __func__, info->CaMmi, + info->CaInitializationStatus, info->CaErrorFlag, + info->CaDvbFlag, info->CaModulePresentStatus, + info->CaApplicationInfo, + info->CaDateTimeRequest, info->CaPmtReply); + return info->CaInitializationStatus == 1 && + info->CaErrorFlag == 0 && + info->CaDvbFlag == 1 && + info->CaModulePresentStatus == 1; +} + +static int firesat_get_ca_flags(ANTENNA_INPUT_INFO *info) +{ + int flags = 0; + if (info->CaModulePresentStatus == 1) + flags |= CA_CI_MODULE_PRESENT; + if (info->CaInitializationStatus == 1 && + info->CaErrorFlag == 0 && + info->CaDvbFlag == 1) + flags |= CA_CI_MODULE_READY; + return flags; +} + +static int firesat_ca_reset(struct firesat *firesat) +{ + if (ca_debug) + printk(KERN_INFO "%s: ioctl CA_RESET\n", __func__); + if (avc_ca_reset(firesat)) + return -EFAULT; + return 0; +} + +static int firesat_ca_get_caps(struct firesat *firesat, void *arg) +{ + struct ca_caps *cap_p = (struct ca_caps*)arg; + int err = 0; + + cap_p->slot_num = 1; + cap_p->slot_type = CA_CI; + cap_p->descr_num = 1; + cap_p->descr_type = CA_ECD; + if (ca_debug) + printk(KERN_INFO "%s: ioctl CA_GET_CAP\n", __func__); + return err; +} + +static int firesat_ca_get_slot_info(struct firesat *firesat, void *arg) +{ + ANTENNA_INPUT_INFO info; + struct ca_slot_info *slot_p = (struct ca_slot_info*)arg; + + if (ca_debug) + printk(KERN_INFO "%s: ioctl CA_GET_SLOT_INFO on slot %d.\n", + __func__, slot_p->num); + if (AVCTunerStatus(firesat, &info)) + return -EFAULT; + + if (slot_p->num == 0) { + slot_p->type = CA_CI; + slot_p->flags = firesat_get_ca_flags(&info); + } + else { + return -EFAULT; + } + return 0; +} + +static int firesat_ca_app_info(struct firesat *firesat, void *arg) +{ + struct ca_msg *reply_p = (struct ca_msg*)arg; + int i; + + if (avc_ca_app_info(firesat, reply_p->msg, &reply_p->length)) + return -EFAULT; + if (ca_debug) { + printk(KERN_INFO "%s: Creating TAG_APP_INFO message:", + __func__); + for (i = 0; i < reply_p->length; i++) + printk("0x%02X, ", (unsigned char)reply_p->msg[i]); + printk("\n"); + } + return 0; +} + +static int firesat_ca_info(struct firesat *firesat, void *arg) +{ + struct ca_msg *reply_p = (struct ca_msg*)arg; + int i; + + if (avc_ca_info(firesat, reply_p->msg, &reply_p->length)) + return -EFAULT; + if (ca_debug) { + printk(KERN_INFO "%s: Creating TAG_CA_INFO message:", + __func__); + for (i = 0; i < reply_p->length; i++) + printk("0x%02X, ", (unsigned char)reply_p->msg[i]); + printk("\n"); + } + return 0; +} + +static int firesat_ca_get_mmi(struct firesat *firesat, void *arg) +{ + struct ca_msg *reply_p = (struct ca_msg*)arg; + int i; + + if (avc_ca_get_mmi(firesat, reply_p->msg, &reply_p->length)) + return -EFAULT; + if (ca_debug) { + printk(KERN_INFO "%s: Creating MMI reply INFO message:", + __func__); + for (i = 0; i < reply_p->length; i++) + printk("0x%02X, ", (unsigned char)reply_p->msg[i]); + printk("\n"); + } + return 0; +} + +static int firesat_ca_get_msg(struct firesat *firesat, void *arg) +{ + int err; + ANTENNA_INPUT_INFO info; + + switch (firesat->ca_last_command) { + case TAG_APP_INFO_ENQUIRY: + err = firesat_ca_app_info(firesat, arg); + break; + case TAG_CA_INFO_ENQUIRY: + err = firesat_ca_info(firesat, arg); break; - case CA_GET_CAP: { - ca_caps_t *cap=(ca_caps_t*)parg; - cap->slot_num = 1; - cap->slot_type = CA_CI_LINK; - cap->descr_num = 1; - cap->descr_type = CA_DSS; + default: + if (AVCTunerStatus(firesat, &info)) + err = -EFAULT; + else if (info.CaMmi == 1) { + err = firesat_ca_get_mmi(firesat, arg); + } + else { + printk(KERN_INFO "%s: Unhandled message 0x%08X\n", + __func__, firesat->ca_last_command); + err = -EFAULT; + } + } + firesat->ca_last_command = 0; + return err; +} +static int firesat_ca_pmt(struct firesat *firesat, void *arg) +{ + struct ca_msg *msg_p = (struct ca_msg*)arg; + int data_pos; + + if (msg_p->msg[3] & 0x80) + data_pos = (msg_p->msg[4] && 0x7F) + 4; + else + data_pos = 4; + if (avc_ca_pmt(firesat, &msg_p->msg[data_pos], + msg_p->length - data_pos)) + return -EFAULT; + return 0; +} + +static int firesat_ca_send_msg(struct firesat *firesat, void *arg) +{ + int err; + struct ca_msg *msg_p = (struct ca_msg*)arg; + + // Do we need a semaphore for this? + firesat->ca_last_command = + (msg_p->msg[0] << 16) + (msg_p->msg[1] << 8) + msg_p->msg[2]; + switch (firesat->ca_last_command) { + case TAG_CA_PMT: + if (ca_debug != 0) + printk(KERN_INFO "%s: Message received: TAG_CA_PMT\n", + __func__); + err = firesat_ca_pmt(firesat, arg); + break; + case TAG_APP_INFO_ENQUIRY: + // This is all handled in ca_get_msg + if (ca_debug != 0) + printk(KERN_INFO "%s: Message received: " + "TAG_APP_INFO_ENQUIRY\n", __func__); err = 0; break; - } - case CA_GET_SLOT_INFO: { - ca_slot_info_t *slot=(ca_slot_info_t*)parg; - if(slot->num == 0) { - slot->type = CA_CI | CA_CI_LINK | CA_DESCR; - slot->flags = CA_CI_MODULE_PRESENT | CA_CI_MODULE_READY; - } else { - slot->type = 0; - slot->flags = 0; - } + case TAG_CA_INFO_ENQUIRY: + // This is all handled in ca_get_msg + if (ca_debug != 0) + printk(KERN_INFO "%s: Message received: " + "TAG_CA_APP_INFO_ENQUIRY\n", __func__); err = 0; break; + case TAG_ENTER_MENU: + if (ca_debug != 0) + printk(KERN_INFO "%s: Entering CA menu.\n", __func__); + err = avc_ca_enter_menu(firesat); + break; + default: + printk(KERN_ERR "%s: Unhandled unknown message 0x%08X\n", + __func__, firesat->ca_last_command); + err = -EFAULT; } + return err; +} + +static int firesat_ca_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct dvb_device* dvbdev = (struct dvb_device*) file->private_data; + struct firesat *firesat = dvbdev->priv; + int err; + ANTENNA_INPUT_INFO info; + + switch(cmd) { + case CA_RESET: + err = firesat_ca_reset(firesat); + break; + case CA_GET_CAP: + err = firesat_ca_get_caps(firesat, arg); + break; + case CA_GET_SLOT_INFO: + err = firesat_ca_get_slot_info(firesat, arg); + break; + case CA_GET_MSG: + err = firesat_ca_get_msg(firesat, arg); + break; + case CA_SEND_MSG: + err = firesat_ca_send_msg(firesat, arg); + break; default: - err=-EINVAL; + printk(KERN_INFO "%s: Unhandled ioctl, command: %u\n",__func__, + cmd); + err = -EOPNOTSUPP; } + + if (AVCTunerStatus(firesat, &info)) + return err; + + firesat_ca_ready(&info); + return err; } -*/ -static int firesat_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - //return dvb_usercopy(inode, file, cmd, arg, firesat_ca_do_ioctl); - return dvb_generic_ioctl(inode, file, cmd, arg); +static int firesat_get_date_time_request(struct firesat *firesat) +{ + if (ca_debug) + printk(KERN_INFO "%s: Retrieving Time/Date request\n", + __func__); + if (avc_ca_get_time_date(firesat, &firesat->ca_time_interval)) + return -EFAULT; + if (ca_debug) + printk(KERN_INFO "%s: Time/Date interval is %d\n", + __func__, firesat->ca_time_interval); + + return 0; } -static int firesat_ca_io_open(struct inode *inode, struct file *file) { - printk(KERN_INFO "%s!\n",__func__); +static int firesat_ca_io_open(struct inode *inode, struct file *file) +{ + if (ca_debug != 0) + printk(KERN_INFO "%s\n",__func__); return dvb_generic_open(inode, file); } -static int firesat_ca_io_release(struct inode *inode, struct file *file) { - printk(KERN_INFO "%s!\n",__func__); +static int firesat_ca_io_release(struct inode *inode, struct file *file) +{ + if (ca_debug != 0) + printk(KERN_INFO "%s\n",__func__); return dvb_generic_release(inode, file); } -static unsigned int firesat_ca_io_poll(struct file *file, poll_table *wait) { -// printk(KERN_INFO "%s!\n",__func__); +static unsigned int firesat_ca_io_poll(struct file *file, poll_table *wait) +{ + if (ca_debug != 0) + printk(KERN_INFO "%s\n",__func__); return POLLIN; } @@ -68,7 +305,7 @@ static struct file_operations firesat_ca_fops = { .owner = THIS_MODULE, .read = NULL, // There is no low level read anymore .write = NULL, // There is no low level write anymore - .ioctl = firesat_ca_ioctl, + .ioctl = dvb_generic_ioctl, .open = firesat_ca_io_open, .release = firesat_ca_io_release, .poll = firesat_ca_io_poll, @@ -80,16 +317,37 @@ static struct dvb_device firesat_ca = { .readers = 1, .writers = 1, .fops = &firesat_ca_fops, + .kernel_ioctl = firesat_ca_ioctl, }; -int firesat_ca_init(struct firesat *firesat) { - int ret = dvb_register_device(firesat->adapter, &firesat->cadev, &firesat_ca, firesat, DVB_DEVICE_CA); - if(ret) return ret; +int firesat_ca_init(struct firesat *firesat) +{ + int err; + ANTENNA_INPUT_INFO info; - // avoid unnecessary delays, we're not talking to the CI yet anyways - return 0; + if (AVCTunerStatus(firesat, &info)) + return -EINVAL; + + if (firesat_ca_ready(&info)) { + err = dvb_register_device(firesat->adapter, + &firesat->cadev, + &firesat_ca, firesat, + DVB_DEVICE_CA); + + if (info.CaApplicationInfo == 0) + printk(KERN_ERR "%s: CaApplicationInfo is not set.\n", + __func__); + if (info.CaDateTimeRequest == 1) + firesat_get_date_time_request(firesat); + } + else + err = -EFAULT; + + return err; } -void firesat_ca_release(struct firesat *firesat) { +void firesat_ca_release(struct firesat *firesat) +{ + if (firesat->cadev) dvb_unregister_device(firesat->cadev); } diff --git a/drivers/media/dvb/firesat/firesat.h b/drivers/media/dvb/firesat/firesat.h index d1e2ce37063e..1beed177d98b 100644 --- a/drivers/media/dvb/firesat/firesat.h +++ b/drivers/media/dvb/firesat/firesat.h @@ -1,3 +1,15 @@ +/* + * FireSAT DVB driver + * + * Copyright (c) ? + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + #ifndef __FIRESAT_H #define __FIRESAT_H @@ -6,15 +18,108 @@ #include "dvb_demux.h" #include "dvb_net.h" +#include <linux/version.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) #include <linux/semaphore.h> +#endif #include <linux/dvb/frontend.h> #include <linux/dvb/dmx.h> +#include <iso.h> + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) +#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w, v) +#else +#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w) +#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(x) +#endif + +/***************************************************************** + * CA message command constants from en50221_app_tags.h of libdvb + *****************************************************************/ +/* Resource Manager */ +#define TAG_PROFILE_ENQUIRY 0x9f8010 +#define TAG_PROFILE 0x9f8011 +#define TAG_PROFILE_CHANGE 0x9f8012 + +/* Application Info */ +#define TAG_APP_INFO_ENQUIRY 0x9f8020 +#define TAG_APP_INFO 0x9f8021 +#define TAG_ENTER_MENU 0x9f8022 + +/* CA Support */ +#define TAG_CA_INFO_ENQUIRY 0x9f8030 +#define TAG_CA_INFO 0x9f8031 +#define TAG_CA_PMT 0x9f8032 +#define TAG_CA_PMT_REPLY 0x9f8033 + +/* Host Control */ +#define TAG_TUNE 0x9f8400 +#define TAG_REPLACE 0x9f8401 +#define TAG_CLEAR_REPLACE 0x9f8402 +#define TAG_ASK_RELEASE 0x9f8403 + +/* Date and Time */ +#define TAG_DATE_TIME_ENQUIRY 0x9f8440 +#define TAG_DATE_TIME 0x9f8441 + +/* Man Machine Interface (MMI) */ +#define TAG_CLOSE_MMI 0x9f8800 +#define TAG_DISPLAY_CONTROL 0x9f8801 +#define TAG_DISPLAY_REPLY 0x9f8802 +#define TAG_TEXT_LAST 0x9f8803 +#define TAG_TEXT_MORE 0x9f8804 +#define TAG_KEYPAD_CONTROL 0x9f8805 +#define TAG_KEYPRESS 0x9f8806 +#define TAG_ENQUIRY 0x9f8807 +#define TAG_ANSWER 0x9f8808 +#define TAG_MENU_LAST 0x9f8809 +#define TAG_MENU_MORE 0x9f880a +#define TAG_MENU_ANSWER 0x9f880b +#define TAG_LIST_LAST 0x9f880c +#define TAG_LIST_MORE 0x9f880d +#define TAG_SUBTITLE_SEGMENT_LAST 0x9f880e +#define TAG_SUBTITLE_SEGMENT_MORE 0x9f880f +#define TAG_DISPLAY_MESSAGE 0x9f8810 +#define TAG_SCENE_END_MARK 0x9f8811 +#define TAG_SCENE_DONE 0x9f8812 +#define TAG_SCENE_CONTROL 0x9f8813 +#define TAG_SUBTITLE_DOWNLOAD_LAST 0x9f8814 +#define TAG_SUBTITLE_DOWNLOAD_MORE 0x9f8815 +#define TAG_FLUSH_DOWNLOAD 0x9f8816 +#define TAG_DOWNLOAD_REPLY 0x9f8817 + +/* Low Speed Communications */ +#define TAG_COMMS_COMMAND 0x9f8c00 +#define TAG_CONNECTION_DESCRIPTOR 0x9f8c01 +#define TAG_COMMS_REPLY 0x9f8c02 +#define TAG_COMMS_SEND_LAST 0x9f8c03 +#define TAG_COMMS_SEND_MORE 0x9f8c04 +#define TAG_COMMS_RECV_LAST 0x9f8c05 +#define TAG_COMMS_RECV_MORE 0x9f8c06 + +/* Authentication */ +#define TAG_AUTH_REQ 0x9f8200 +#define TAG_AUTH_RESP 0x9f8201 + +/* Teletext */ +#define TAG_TELETEXT_EBU 0x9f9000 + +/* Smartcard */ +#define TAG_SMARTCARD_COMMAND 0x9f8e00 +#define TAG_SMARTCARD_REPLY 0x9f8e01 +#define TAG_SMARTCARD_SEND 0x9f8e02 +#define TAG_SMARTCARD_RCV 0x9f8e03 + +/* EPG */ +#define TAG_EPG_ENQUIRY 0x9f8f00 +#define TAG_EPG_REPLY 0x9f8f01 + enum model_type { - FireSAT_DVB_S = 1, - FireSAT_DVB_C = 2, - FireSAT_DVB_T = 3, - FireSAT_DVB_S2 = 4 + FireSAT_DVB_S = 1, + FireSAT_DVB_C = 2, + FireSAT_DVB_T = 3, + FireSAT_DVB_S2 = 4 }; struct firesat { @@ -31,12 +136,13 @@ struct firesat { struct dvb_frontend *fe; struct dvb_device *cadev; - int has_ci; + int ca_last_command; + int ca_time_interval; struct semaphore avc_sem; - atomic_t avc_reply_received; + atomic_t avc_reply_received; - atomic_t reschedule_remotecontrol; + atomic_t reschedule_remotecontrol; struct firesat_channel { struct firesat *firesat; @@ -53,20 +159,54 @@ struct firesat { void *respfrm; int resp_length; -// nodeid_t nodeid; - struct hpsb_host *host; + struct hpsb_host *host; u64 guid; /* GUID of this node */ u32 guid_vendor_id; /* Top 24bits of guid */ struct node_entry *nodeentry; - enum model_type type; - char subunit; + enum model_type type; + char subunit; fe_sec_voltage_t voltage; fe_sec_tone_mode_t tone; int isochannel; + struct hpsb_iso *iso_handle; + + struct list_head list; +}; + +struct firewireheader { + union { + struct { + __u8 tcode:4; + __u8 sy:4; + __u8 tag:2; + __u8 channel:6; + + __u8 length_l; + __u8 length_h; + } hdr; + __u32 val; + }; +}; - struct list_head list; +struct CIPHeader { + union { + struct { + __u8 syncbits:2; + __u8 sid:6; + __u8 dbs; + __u8 fn:2; + __u8 qpc:3; + __u8 sph:1; + __u8 rsv:2; + __u8 dbc; + __u8 syncbits2:2; + __u8 fmt:6; + __u32 fdf:24; + } cip; + __u64 val; + }; }; extern struct list_head firesat_list; @@ -76,11 +216,15 @@ extern spinlock_t firesat_list_lock; extern int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed); extern int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed); extern int firesat_dvbdev_init(struct firesat *firesat, - struct device *dev, - struct dvb_frontend *fe); + struct device *dev, + struct dvb_frontend *fe); /* firesat_fe.c */ -extern int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe); +extern int firesat_frontend_attach(struct firesat *firesat, + struct dvb_frontend *fe); +/* firesat_iso.c */ +extern int setup_iso_channel(struct firesat *firesat); +extern void tear_down_iso_channel(struct firesat *firesat); #endif diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c index dcac70a2991e..04ad31666fb9 100644 --- a/drivers/media/dvb/firesat/firesat_1394.c +++ b/drivers/media/dvb/firesat/firesat_1394.c @@ -3,6 +3,7 @@ * * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com> * Copyright (c) 2007-2008 Ben Backx <ben@bbackx.com> + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -18,7 +19,6 @@ #include <linux/time.h> #include <linux/errno.h> #include <linux/interrupt.h> -#include <linux/semaphore.h> #include <ieee1394_hotplug.h> #include <nodemgr.h> #include <highlevel.h> @@ -79,11 +79,6 @@ static void firesat_add_host(struct hpsb_host *host); static void firesat_remove_host(struct hpsb_host *host); static void firesat_host_reset(struct hpsb_host *host); -/* -static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data, - size_t length); -*/ - static void fcp_request(struct hpsb_host *host, int nodeid, int direction, @@ -96,7 +91,6 @@ static struct hpsb_highlevel firesat_highlevel = { .add_host = firesat_add_host, .remove_host = firesat_remove_host, .host_reset = firesat_host_reset, -// FIXME .iso_receive = iso_receive, .fcp_request = fcp_request, }; @@ -127,100 +121,6 @@ static void firesat_host_reset(struct hpsb_host *host) printk(KERN_INFO "FireSAT host_reset (nodeid = 0x%x, hosts active = %d)\n",host->node_id,host->nodes_active); } -struct firewireheader { - union { - struct { - unsigned char tcode:4; - unsigned char sy:4; - unsigned char tag:2; - unsigned char channel:6; - - unsigned char length_l; - unsigned char length_h; - } hdr; - unsigned long val; - }; -}; - -struct CIPHeader { - union { - struct { - unsigned char syncbits:2; - unsigned char sid:6; - unsigned char dbs; - unsigned char fn:2; - unsigned char qpc:3; - unsigned char sph:1; - unsigned char rsv:2; - unsigned char dbc; - unsigned char syncbits2:2; - unsigned char fmt:6; - unsigned long fdf:24; - } cip; - unsigned long long val; - }; -}; - -struct MPEG2Header { - union { - struct { - unsigned char sync; // must be 0x47 - unsigned char transport_error_indicator:1; - unsigned char payload_unit_start_indicator:1; - unsigned char transport_priority:1; - unsigned short pid:13; - unsigned char transport_scrambling_control:2; - unsigned char adaption_field_control:2; - unsigned char continuity_counter:4; - } hdr; - unsigned long val; - }; -}; - -#if 0 -static void iso_receive(struct hpsb_host *host, - int channel, - quadlet_t *data, - size_t length) -{ - struct firesat *firesat = NULL; - struct firesat *firesat_entry; - unsigned long flags; - -// printk(KERN_INFO "FireSAT iso_receive: channel %d, length = %d\n", channel, length); - - if (length <= 12) - return; // ignore empty packets - else { - - spin_lock_irqsave(&firesat_list_lock, flags); - list_for_each_entry(firesat_entry,&firesat_list,list) { - if(firesat_entry->host == host && firesat_entry->isochannel == channel) { - firesat=firesat_entry; - break; - } - } - spin_unlock_irqrestore(&firesat_list_lock, flags); - - if (firesat) { - char *buf= ((char*)data) + sizeof(struct firewireheader)+sizeof(struct CIPHeader); - int count = (length-sizeof(struct CIPHeader)) / 192; - -// printk(KERN_INFO "%s: length = %u\n data[0] = %08x\n data[1] = %08x\n data[2] = %08x\n data[3] = %08x\n data[4] = %08x\n",__func__, length, data[0],data[1],data[2],data[3],data[4]); - - while (count--) { - - if (buf[sizeof(quadlet_t) /*timestamp*/] == 0x47) - dvb_dmx_swfilter_packets(&firesat->demux, &buf[sizeof(quadlet_t)], 1); - else - printk("%s: invalid packet, skipping\n", __func__); - buf += 188 + sizeof (quadlet_t) /* timestamp */; - } - } - } -} -#endif - static void fcp_request(struct hpsb_host *host, int nodeid, int direction, @@ -251,7 +151,9 @@ static void fcp_request(struct hpsb_host *host, AVCRecv(firesat,data,length); else printk("%s: received fcp request from unknown source, ignored\n", __func__); - } // else ignore + } + else + printk("%s: received invalid fcp request, ignored\n", __func__); } static int firesat_probe(struct device *dev) @@ -260,7 +162,6 @@ static int firesat_probe(struct device *dev) struct firesat *firesat; struct dvb_frontend *fe; unsigned long flags; - int result; unsigned char subunitcount = 0xff, subunit; struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL); int kv_len; @@ -298,6 +199,7 @@ static int firesat_probe(struct device *dev) firesat->isochannel = -1; firesat->tone = 0xff; firesat->voltage = 0xff; + firesat->fe = fe; if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) { printk("%s: couldn't allocate memory.\n", __func__); @@ -357,7 +259,7 @@ static int firesat_probe(struct device *dev) } kfree(kv_buf); - if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type, &firesat->has_ci)) { + if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type)) { printk("%s: cannot identify subunit %d\n", __func__, subunit); spin_lock_irqsave(&firesat_list_lock, flags); list_del(&firesat->list); @@ -382,7 +284,6 @@ static int firesat_probe(struct device *dev) static int firesat_remove(struct device *dev) { struct unit_directory *ud = container_of(dev, struct unit_directory, device); - struct dvb_frontend* fe; struct firesat **firesats = ud->device.driver_data; int k; unsigned long flags; @@ -390,18 +291,9 @@ static int firesat_remove(struct device *dev) if (firesats) { for (k = 0; k < 2; k++) if (firesats[k]) { - if (firesats[k]->has_ci) firesat_ca_release(firesats[k]); -#if 0 - if (!(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) { - fe->ops = firesat_ops; - fe->dvb = firesats[k]->adapter; - - dvb_unregister_frontend(fe); - kfree(fe); - } -#endif + dvb_unregister_frontend(firesats[k]->fe); dvb_net_release(&firesats[k]->dvbnet); firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx); firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend); @@ -413,6 +305,7 @@ static int firesat_remove(struct device *dev) list_del(&firesats[k]->list); spin_unlock_irqrestore(&firesat_list_lock, flags); + kfree(firesats[k]->fe); kfree(firesats[k]->adapter); kfree(firesats[k]->respfrm); kfree(firesats[k]); diff --git a/drivers/media/dvb/firesat/firesat_dvb.c b/drivers/media/dvb/firesat/firesat_dvb.c index 38aad0812881..9e87402289a6 100644 --- a/drivers/media/dvb/firesat/firesat_dvb.c +++ b/drivers/media/dvb/firesat/firesat_dvb.c @@ -1,3 +1,15 @@ +/* + * FireSAT DVB driver + * + * Copyright (c) ? + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + #include <linux/init.h> #include <linux/slab.h> #include <linux/wait.h> @@ -6,7 +18,6 @@ #include <linux/time.h> #include <linux/errno.h> #include <linux/interrupt.h> -#include <linux/semaphore.h> #include <ieee1394_hotplug.h> #include <nodemgr.h> #include <highlevel.h> @@ -26,13 +37,13 @@ static struct firesat_channel *firesat_channel_allocate(struct firesat *firesat) { int k; - printk(KERN_INFO "%s\n", __func__); + //printk(KERN_INFO "%s\n", __func__); if (down_interruptible(&firesat->demux_sem)) return NULL; for (k = 0; k < 16; k++) { - printk(KERN_INFO "%s: channel %d: active = %d, pid = 0x%x\n",__func__,k,firesat->channel[k].active,firesat->channel[k].pid); + //printk(KERN_INFO "%s: channel %d: active = %d, pid = 0x%x\n",__func__,k,firesat->channel[k].active,firesat->channel[k].pid); if (firesat->channel[k].active == 0) { firesat->channel[k].active = 1; @@ -82,14 +93,15 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) int pidc,k; u16 pids[16]; - printk(KERN_INFO "%s (pid %u)\n",__func__,dvbdmxfeed->pid); +// printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid); switch (dvbdmxfeed->type) { case DMX_TYPE_TS: case DMX_TYPE_SEC: break; default: - printk("%s: invalid type %u\n",__func__,dvbdmxfeed->type); + printk(KERN_ERR "%s: invalid type %u\n", + __func__, dvbdmxfeed->type); return -EINVAL; } @@ -110,7 +122,8 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) channel = firesat_channel_allocate(firesat); break; default: - printk("%s: invalid pes type %u\n",__func__, dvbdmxfeed->pes_type); + printk(KERN_ERR "%s: invalid pes type %u\n", + __func__, dvbdmxfeed->pes_type); return -EINVAL; } } else { @@ -118,7 +131,7 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) } if (!channel) { - printk("%s: busy!\n", __func__); + printk(KERN_ERR "%s: busy!\n", __func__); return -EBUSY; } @@ -131,22 +144,23 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) if (firesat_channel_collect(firesat, &pidc, pids)) { firesat_channel_release(firesat, channel); + printk(KERN_ERR "%s: could not collect pids!\n", __func__); return -EINTR; } if(dvbdmxfeed->pid == 8192) { - if((k=AVCTuner_GetTS(firesat))) { + if((k = AVCTuner_GetTS(firesat))) { firesat_channel_release(firesat, channel); printk("%s: AVCTuner_GetTS failed with error %d\n", - __func__,k); + __func__, k); return k; } } else { - if((k=AVCTuner_SetPIDs(firesat, pidc, pids))) { + if((k = AVCTuner_SetPIDs(firesat, pidc, pids))) { firesat_channel_release(firesat, channel); printk("%s: AVCTuner_SetPIDs failed with error %d\n", - __func__,k); + __func__, k); return k; } } @@ -161,7 +175,7 @@ int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) int k, l = 0; u16 pids[16]; - printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid); + //printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid); if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) && (demux->dmx.frontend->source != DMX_MEMORY_FE))) { @@ -189,12 +203,13 @@ int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) // list except channel to be removed for (k = 0; k < 16; k++) - if (firesat->channel[k].active == 1) + if (firesat->channel[k].active == 1) { if (&firesat->channel[k] != (struct firesat_channel *)dvbdmxfeed->priv) pids[l++] = firesat->channel[k].pid; else firesat->channel[k].active = 0; + } if ((k = AVCTuner_SetPIDs(firesat, l, pids))) { up(&firesat->demux_sem); @@ -214,8 +229,6 @@ int firesat_dvbdev_init(struct firesat *firesat, { int result; - firesat->has_ci = 1; // TEMP workaround - #if 0 switch (firesat->type) { case FireSAT_DVB_S: @@ -254,7 +267,7 @@ int firesat_dvbdev_init(struct firesat *firesat, return -ENOMEM; } - if ((result = dvb_register_adapter(firesat->adapter, + if ((result = DVB_REGISTER_ADAPTER(firesat->adapter, firesat->model_name, THIS_MODULE, dev, adapter_nr)) < 0) { @@ -271,6 +284,7 @@ int firesat_dvbdev_init(struct firesat *firesat, return result; } + memset(&firesat->demux, 0, sizeof(struct dvb_demux)); firesat->demux.dmx.capabilities = 0/*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/; firesat->demux.priv = (void *)firesat; @@ -343,8 +357,9 @@ int firesat_dvbdev_init(struct firesat *firesat, return result; } - if (firesat->has_ci) firesat_ca_init(firesat); return 0; } + + diff --git a/drivers/media/dvb/firesat/firesat_fe.c b/drivers/media/dvb/firesat/firesat_fe.c index f7abd38f0014..1c86c3e61373 100644 --- a/drivers/media/dvb/firesat/firesat_fe.c +++ b/drivers/media/dvb/firesat/firesat_fe.c @@ -1,3 +1,15 @@ +/* + * FireSAT DVB driver + * + * Copyright (c) ? + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + #include <linux/init.h> #include <linux/slab.h> #include <linux/wait.h> @@ -6,7 +18,6 @@ #include <linux/time.h> #include <linux/errno.h> #include <linux/interrupt.h> -#include <linux/semaphore.h> #include <ieee1394_hotplug.h> #include <nodemgr.h> #include <highlevel.h> @@ -22,22 +33,29 @@ static int firesat_dvb_init(struct dvb_frontend *fe) { + int result; struct firesat *firesat = fe->sec_priv; - printk("fdi: 1\n"); +// printk("fdi: 1\n"); firesat->isochannel = firesat->adapter->num; //<< 1 | (firesat->subunit & 0x1); // ### ask IRM - printk("fdi: 2\n"); - try_CMPEstablishPPconnection(firesat, firesat->subunit, firesat->isochannel); - printk("fdi: 3\n"); -//FIXME hpsb_listen_channel(&firesat_highlevel, firesat->host, firesat->isochannel); - printk("fdi: 4\n"); - return 0; +// printk("fdi: 2\n"); + result = try_CMPEstablishPPconnection(firesat, firesat->subunit, firesat->isochannel); + if (result != 0) { + printk(KERN_ERR "Could not establish point to point " + "connection.\n"); + return -1; + } +// printk("fdi: 3\n"); + + result = setup_iso_channel(firesat); +// printk("fdi: 4. Result was %d\n", result); + return result; } static int firesat_sleep(struct dvb_frontend *fe) { struct firesat *firesat = fe->sec_priv; -//FIXME hpsb_unlisten_channel(&firesat_highlevel, firesat->host, firesat->isochannel); + tear_down_iso_channel(firesat); try_CMPBreakPPconnection(firesat, firesat->subunit, firesat->isochannel); firesat->isochannel = -1; return 0; @@ -83,19 +101,20 @@ static int firesat_read_status (struct dvb_frontend *fe, fe_status_t *status) if (AVCTunerStatus(firesat, &info)) return -EINVAL; - if (info.NoRF) + if (info.NoRF) { *status = 0; - else - *status = *status = FE_HAS_SIGNAL | - FE_HAS_VITERBI | - FE_HAS_SYNC | - FE_HAS_CARRIER | - FE_HAS_LOCK; + } else { + *status = FE_HAS_SIGNAL | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_CARRIER | + FE_HAS_LOCK; + } return 0; } -static int firesat_read_ber (struct dvb_frontend *fe, u32 *ber) +static int firesat_read_ber(struct dvb_frontend *fe, u32 *ber) { struct firesat *firesat = fe->sec_priv; ANTENNA_INPUT_INFO info; @@ -103,10 +122,10 @@ static int firesat_read_ber (struct dvb_frontend *fe, u32 *ber) if (AVCTunerStatus(firesat, &info)) return -EINVAL; - *ber = ((info.BER[0] << 24) & 0xff) | - ((info.BER[1] << 16) & 0xff) | - ((info.BER[2] << 8) & 0xff) | - (info.BER[3] & 0xff); + *ber = (info.BER[0] << 24) | + (info.BER[1] << 16) | + (info.BER[2] << 8) | + info.BER[3]; return 0; } @@ -115,19 +134,29 @@ static int firesat_read_signal_strength (struct dvb_frontend *fe, u16 *strength) { struct firesat *firesat = fe->sec_priv; ANTENNA_INPUT_INFO info; - u16 *signal = strength; if (AVCTunerStatus(firesat, &info)) return -EINVAL; - *signal = info.SignalStrength; + *strength = info.SignalStrength << 8; return 0; } static int firesat_read_snr(struct dvb_frontend *fe, u16 *snr) { - return -EOPNOTSUPP; + struct firesat *firesat = fe->sec_priv; + ANTENNA_INPUT_INFO info; + + if (AVCTunerStatus(firesat, &info)) + return -EINVAL; + + *snr = (info.CarrierNoiseRatio[0] << 8) + + info.CarrierNoiseRatio[1]; + *snr *= 257; + // C/N[dB] = -10 * log10(snr / 65535) + + return 0; } static int firesat_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) @@ -192,14 +221,13 @@ int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe) firesat->frontend_info = &firesat_T_frontend_info; break; default: -// printk("%s: unknown model type 0x%x on subunit %d!\n", -// __func__, firesat->type,subunit); printk("%s: unknown model type 0x%x !\n", __func__, firesat->type); firesat->model_name = "Unknown"; firesat->frontend_info = NULL; } fe->ops = firesat_ops; + fe->ops.info = *(firesat->frontend_info); fe->dvb = firesat->adapter; return 0; diff --git a/drivers/media/dvb/firesat/firesat_iso.c b/drivers/media/dvb/firesat/firesat_iso.c new file mode 100644 index 000000000000..15e23cf7d503 --- /dev/null +++ b/drivers/media/dvb/firesat/firesat_iso.c @@ -0,0 +1,106 @@ +/* + * FireSAT DVB driver + * + * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include "firesat.h" + +static void rawiso_activity_cb(struct hpsb_iso *iso); + +void tear_down_iso_channel(struct firesat *firesat) +{ + if (firesat->iso_handle != NULL) { + hpsb_iso_stop(firesat->iso_handle); + hpsb_iso_shutdown(firesat->iso_handle); + } + firesat->iso_handle = NULL; +} + +int setup_iso_channel(struct firesat *firesat) +{ + int result; + firesat->iso_handle = + hpsb_iso_recv_init(firesat->host, + 256 * 200, //data_buf_size, + 256, //buf_packets, + firesat->isochannel, + HPSB_ISO_DMA_DEFAULT, //dma_mode, + -1, //stat.config.irq_interval, + rawiso_activity_cb); + if (firesat->iso_handle == NULL) { + printk(KERN_ERR "Cannot initialize iso receive.\n"); + return -EINVAL; + } + result = hpsb_iso_recv_start(firesat->iso_handle, -1, -1, 0); + if (result != 0) { + printk(KERN_ERR "Cannot start iso receive.\n"); + return -EINVAL; + } + return 0; +} + +static void rawiso_activity_cb(struct hpsb_iso *iso) +{ + unsigned int num; + unsigned int i; +/* unsigned int j; */ + unsigned int packet; + unsigned long flags; + struct firesat *firesat = NULL; + struct firesat *firesat_iterator; + + spin_lock_irqsave(&firesat_list_lock, flags); + list_for_each_entry(firesat_iterator, &firesat_list, list) { + if(firesat_iterator->iso_handle == iso) { + firesat = firesat_iterator; + break; + } + } + spin_unlock_irqrestore(&firesat_list_lock, flags); + + if (firesat) { + packet = iso->first_packet; + num = hpsb_iso_n_ready(iso); + for (i = 0; i < num; i++, + packet = (packet + 1) % iso->buf_packets) { + unsigned char *buf = + dma_region_i(&iso->data_buf, unsigned char, + iso->infos[packet].offset + + sizeof(struct CIPHeader)); + int count = (iso->infos[packet].len - + sizeof(struct CIPHeader)) / + (188 + sizeof(struct firewireheader)); + if (iso->infos[packet].len <= sizeof(struct CIPHeader)) + continue; // ignore empty packet +/* printk("%s: Handling packets (%d): ", __func__, */ +/* iso->infos[packet].len); */ +/* for (j = 0; j < iso->infos[packet].len - */ +/* sizeof(struct CIPHeader); j++) */ +/* printk("%02X,", buf[j]); */ +/* printk("\n"); */ + while (count --) { + if (buf[sizeof(struct firewireheader)] == 0x47) + dvb_dmx_swfilter_packets(&firesat->demux, + &buf[sizeof(struct firewireheader)], 1); + else + printk("%s: invalid packet, skipping\n", __func__); + buf += 188 + sizeof(struct firewireheader); + + } + + } + hpsb_iso_recv_release_packets(iso, num); + } + else { + printk("%s: packets for unknown iso channel, skipping\n", + __func__); + hpsb_iso_recv_release_packets(iso, hpsb_iso_n_ready(iso)); + } +} + -- cgit v1.2.3 From 81c67b7f82769292a86b802590be5879413f9278 Mon Sep 17 00:00:00 2001 From: Henrik Kurelid <henrik@kurelid.se> Date: Sun, 24 Aug 2008 15:20:07 +0200 Subject: firesat: avc resend - Add resending of AVC message to the card if no answer is received - Replace the homebrewed event_wait function with a standard wait queue - Clean up of log/error messages - Increase debug level of avc communication Signed-off-by: Henrik Kurelid <henrik@kurelid.se> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/avc_api.c | 173 +++++++++++++++++-------------- drivers/media/dvb/firesat/firesat.h | 1 + drivers/media/dvb/firesat/firesat_1394.c | 1 + 3 files changed, 95 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c index 273c7235dd90..3c8e7e3dacc2 100644 --- a/drivers/media/dvb/firesat/avc_api.c +++ b/drivers/media/dvb/firesat/avc_api.c @@ -26,7 +26,7 @@ static unsigned int avc_comm_debug = 0; module_param(avc_comm_debug, int, 0644); -MODULE_PARM_DESC(avc_comm_debug, "debug logging of AV/C communication, default is 0 (no)"); +MODULE_PARM_DESC(avc_comm_debug, "debug logging level [0..2] of AV/C communication, default is 0 (no)"); static int __AVCRegisterRemoteControl(struct firesat*firesat, int internal); @@ -37,22 +37,6 @@ static void avc_free_packet(struct hpsb_packet *packet) hpsb_free_packet(packet); } -/* - * Goofy routine that basically does a down_timeout function. - * Stolen from sbp2.c - */ -static int avc_down_timeout(atomic_t *done, int timeout) -{ - int i; - - for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) { - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(HZ/10)) /* 100ms */ - return(1); - } - return ((i > 0) ? 0:1); -} - static const char* get_ctype_string(__u8 ctype) { switch(ctype) @@ -135,97 +119,115 @@ static void log_command_frame(const AVCCmdFrm *CmdFrm) { int k; printk(KERN_INFO "AV/C Command Frame:\n"); - printk("CommandType=%s, Address=%s(0x%02X,0x%02X), opcode=%s(0x%02X), " - "length=%d\n", get_ctype_string(CmdFrm->ctype), + printk(KERN_INFO "CommandType=%s, Address=%s(0x%02X,0x%02X), " + "opcode=%s(0x%02X), length=%d\n", + get_ctype_string(CmdFrm->ctype), get_subunit_address(CmdFrm->suid, CmdFrm->sutyp), CmdFrm->suid, CmdFrm->sutyp, get_opcode_string(CmdFrm->opcode), CmdFrm->opcode, CmdFrm->length); - for(k = 0; k < CmdFrm->length - 3; k++) { - if (k % 5 != 0) - printk(", "); - else if (k != 0) - printk("\n"); - printk("operand[%d] = %02X", k, CmdFrm->operand[k]); + if (avc_comm_debug > 1) { + for(k = 0; k < CmdFrm->length - 3; k++) { + if (k % 5 != 0) + printk(", "); + else if (k != 0) + printk("\n"); + printk(KERN_INFO "operand[%d] = %02X", k, + CmdFrm->operand[k]); + } + printk(KERN_INFO "\n"); } - printk("\n"); } static void log_response_frame(const AVCRspFrm *RspFrm) { int k; printk(KERN_INFO "AV/C Response Frame:\n"); - printk("Response=%s, Address=%s(0x%02X,0x%02X), opcode=%s(0x%02X), " - "length=%d\n", get_resp_string(RspFrm->resp), + printk(KERN_INFO "Response=%s, Address=%s(0x%02X,0x%02X), " + "opcode=%s(0x%02X), length=%d\n", get_resp_string(RspFrm->resp), get_subunit_address(RspFrm->suid, RspFrm->sutyp), RspFrm->suid, RspFrm->sutyp, get_opcode_string(RspFrm->opcode), RspFrm->opcode, RspFrm->length); - for(k = 0; k < RspFrm->length - 3; k++) { - if (k % 5 != 0) - printk(", "); - else if (k != 0) - printk("\n"); - printk("operand[%d] = %02X", k, RspFrm->operand[k]); + if (avc_comm_debug > 1) { + for(k = 0; k < RspFrm->length - 3; k++) { + if (k % 5 != 0) + printk(KERN_INFO ", "); + else if (k != 0) + printk(KERN_INFO "\n"); + printk(KERN_INFO "operand[%d] = %02X", k, + RspFrm->operand[k]); + } + printk(KERN_INFO "\n"); } - printk("\n"); } static int __AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) { struct hpsb_packet *packet; struct node_entry *ne; + int num_tries = 0; + int packet_ok = 0; ne = firesat->nodeentry; if(!ne) { - printk("%s: lost node!\n",__func__); + printk(KERN_ERR "%s: lost node!\n",__func__); return -EIO; } /* need all input data */ if(!firesat || !ne || !CmdFrm) { - printk("%s: missing input data!\n",__func__); + printk(KERN_ERR "%s: missing input data!\n",__func__); return -EINVAL; } - if (avc_comm_debug == 1) { + if (avc_comm_debug > 0) { log_command_frame(CmdFrm); } if(RspFrm) atomic_set(&firesat->avc_reply_received, 0); - packet=hpsb_make_writepacket(ne->host, ne->nodeid, - COMMAND_REGISTER, - (quadlet_t*)CmdFrm, - CmdFrm->length); - hpsb_set_packet_complete_task(packet, - (void (*)(void*))avc_free_packet, - packet); - hpsb_node_fill_packet(ne, packet); - - if (hpsb_send_packet(packet) < 0) { - avc_free_packet(packet); - atomic_set(&firesat->avc_reply_received, 1); - printk("%s: send failed!\n",__func__); - return -EIO; - } - - if(RspFrm) { - // AV/C specs say that answers should be send within - // 150 ms so let's time out after 200 ms - if(avc_down_timeout(&firesat->avc_reply_received, - HZ / 5)) { - printk("%s: timeout waiting for avc response\n", - __func__); + while (packet_ok == 0 && num_tries < 6) { + num_tries++; + packet_ok = 1; + packet = hpsb_make_writepacket(ne->host, ne->nodeid, + COMMAND_REGISTER, + (quadlet_t*)CmdFrm, + CmdFrm->length); + hpsb_set_packet_complete_task(packet, + (void (*)(void*))avc_free_packet, + packet); + hpsb_node_fill_packet(ne, packet); + + if (hpsb_send_packet(packet) < 0) { + avc_free_packet(packet); atomic_set(&firesat->avc_reply_received, 1); - return -ETIMEDOUT; + printk(KERN_ERR "%s: send failed!\n",__func__); + return -EIO; } - memcpy(RspFrm, firesat->respfrm, - firesat->resp_length); - RspFrm->length = firesat->resp_length; - if (avc_comm_debug == 1) { - log_response_frame(RspFrm); + + if(RspFrm) { + // AV/C specs say that answers should be send within + // 150 ms so let's time out after 200 ms + if (wait_event_timeout(firesat->avc_wait, + atomic_read(&firesat->avc_reply_received) == 1, + HZ / 5) == 0) { + packet_ok = 0; + } + else { + memcpy(RspFrm, firesat->respfrm, + firesat->resp_length); + RspFrm->length = firesat->resp_length; + if (avc_comm_debug > 0) { + log_response_frame(RspFrm); + } + } } } + if (packet_ok == 0) { + printk(KERN_ERR "%s: AV/C response timed out 6 times.\n", + __func__); + return -ETIMEDOUT; + } return 0; } @@ -292,7 +294,8 @@ int AVCRecv(struct firesat *firesat, u8 *data, size_t length) { } #endif if(atomic_read(&firesat->avc_reply_received) == 1) { - printk("%s: received out-of-order AVC response, ignored\n",__func__); + printk(KERN_ERR "%s: received out-of-order AVC response, " + "ignored\n",__func__); return -EINVAL; } // AVCRspFrm *resp=(AVCRspFrm *)data; @@ -312,6 +315,7 @@ int AVCRecv(struct firesat *firesat, u8 *data, size_t length) { firesat->resp_length=length; atomic_set(&firesat->avc_reply_received, 1); + wake_up(&firesat->avc_wait); return 0; } @@ -740,11 +744,12 @@ int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *tr return -EIO; if(RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { - printk("%s: AVCWrite returned error code %d\n",__func__,RspFrm.resp); + printk(KERN_ERR "%s: AVCWrite returned error code %d\n", + __func__, RspFrm.resp); return -EINVAL; } if(((RspFrm.operand[3] << 8) + RspFrm.operand[4]) != 8) { - printk("%s: Invalid response length\n",__func__); + printk(KERN_ERR "%s: Invalid response length\n", __func__); return -EINVAL; } if(systemId) @@ -777,7 +782,8 @@ int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_in return -EIO; if(RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { - printk("%s: AVCWrite returned code %d\n",__func__,RspFrm.resp); + printk(KERN_ERR "%s: AVCWrite returned code %d\n", + __func__, RspFrm.resp); return -EINVAL; } @@ -788,7 +794,8 @@ int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_in sizeof(ANTENNA_INPUT_INFO)); return 0; } - printk("%s: invalid info returned from AVC\n",__func__); + printk(KERN_ERR "%s: invalid tuner status (op=%d,length=%d) returned " + "from AVC\n", __func__, RspFrm.operand[1], length); return -EINVAL; } @@ -800,7 +807,8 @@ int AVCLNBControl(struct firesat *firesat, char voltage, char burst, AVCRspFrm RspFrm; int i,j; - printk(KERN_INFO "%s: voltage = %x, burst = %x, conttone = %x\n",__func__,voltage,burst,conttone); + printk(KERN_INFO "%s: voltage = %x, burst = %x, conttone = %x\n", + __func__, voltage, burst, conttone); memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); @@ -822,11 +830,13 @@ int AVCLNBControl(struct firesat *firesat, char voltage, char burst, for(j=0;j<nrdiseq;j++) { int k; - printk(KERN_INFO "%s: diseq %d len %x\n",__func__,j,diseqcmd[j].msg_len); + printk(KERN_INFO "%s: diseq %d len %x\n", + __func__, j, diseqcmd[j].msg_len); CmdFrm.operand[i++]=diseqcmd[j].msg_len; for(k=0;k<diseqcmd[j].msg_len;k++) { - printk(KERN_INFO "%s: diseq %d msg[%d] = %x\n",__func__,j,k,diseqcmd[j].msg[k]); + printk(KERN_INFO "%s: diseq %d msg[%d] = %x\n", + __func__, j, k, diseqcmd[j].msg[k]); CmdFrm.operand[i++]=diseqcmd[j].msg[k]; } } @@ -847,7 +857,8 @@ int AVCLNBControl(struct firesat *firesat, char voltage, char burst, return -EIO; if(RspFrm.resp != ACCEPTED) { - printk("%s: AVCWrite returned code %d\n",__func__,RspFrm.resp); + printk(KERN_ERR "%s: AVCWrite returned code %d\n", + __func__, RspFrm.resp); return -EINVAL; } @@ -879,7 +890,8 @@ int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount) return -EIO; if(RspFrm.resp != STABLE) { - printk("%s: AVCWrite returned code %d\n",__func__,RspFrm.resp); + printk(KERN_ERR "%s: AVCWrite returned code %d\n", + __func__, RspFrm.resp); return -EINVAL; } @@ -1100,9 +1112,10 @@ int avc_ca_pmt(struct firesat *firesat, char *msg, int length) CmdFrm.opcode = VENDOR; if (msg[0] != LIST_MANAGEMENT_ONLY) { - printk(KERN_ERR "The only list_manasgement parameter that is " - "supported by the firesat driver is \"only\" (3)."); - return -EFAULT; + printk(KERN_INFO "%s: list_management %d not support. " + "Forcing list_management to \"only\" (3). \n", + __func__, msg[0]); + msg[0] = LIST_MANAGEMENT_ONLY; } // We take the cmd_id from the programme level only! list_management = msg[0]; diff --git a/drivers/media/dvb/firesat/firesat.h b/drivers/media/dvb/firesat/firesat.h index 1beed177d98b..f0bac244783a 100644 --- a/drivers/media/dvb/firesat/firesat.h +++ b/drivers/media/dvb/firesat/firesat.h @@ -140,6 +140,7 @@ struct firesat { int ca_time_interval; struct semaphore avc_sem; + wait_queue_head_t avc_wait; atomic_t avc_reply_received; atomic_t reschedule_remotecontrol; diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c index 04ad31666fb9..b19e59416b59 100644 --- a/drivers/media/dvb/firesat/firesat_1394.c +++ b/drivers/media/dvb/firesat/firesat_1394.c @@ -208,6 +208,7 @@ static int firesat_probe(struct device *dev) } sema_init(&firesat->avc_sem, 1); + init_waitqueue_head(&firesat->avc_wait); atomic_set(&firesat->avc_reply_received, 1); sema_init(&firesat->demux_sem, 1); atomic_set(&firesat->reschedule_remotecontrol, 0); -- cgit v1.2.3 From 612262a53352af839a14b3395975a3440c95080a Mon Sep 17 00:00:00 2001 From: Stefan Richter <stefanr@s5r6.in-berlin.de> Date: Tue, 26 Aug 2008 00:17:30 +0200 Subject: firesat: copyrights, rename to firedtv, API conversions, fix remote control input Combination of the following changes: Tue, 26 Aug 2008 00:17:30 +0200 (CEST) firedtv: fix remote control input and update the scancode-to-keycode mapping to a current model. Per default, various media key keycodes are emitted which closely match what is printed on the remote. Userland can modify the mapping by means of evdev ioctls. (Not tested.) The old scancode-to-keycode mapping is left in the driver but cannot be modified by ioctls. This preserves status quo for old remotes. Tue, 26 Aug 2008 00:11:28 +0200 (CEST) firedtv: replace tasklet by workqueue job Non-atomic context is a lot nicer to work with. Sun, 24 Aug 2008 23:30:00 +0200 (CEST) firedtv: move some code back to ieee1394 core Partially reverts "ieee1394: remove unused code" of Linux 2.6.25. Sun, 24 Aug 2008 23:29:30 +0200 (CEST) firedtv: replace semaphore by mutex firesat->avc_sem and ->demux_sem have been used exactly like a mutex. The only exception is the schedule_remotecontrol tasklet which did a down_trylock in atomic context. This is not possible with mutex_trylock; however the whole remote control related code is non-functional anyway at the moment. This should be fixed eventually, probably by turning the tasklet into a worqueue job. Convert everything else from semaphore to mutex. Also rewrite a few of the affected functions to unlock the mutex at a single exit point, instead of in several branches. Sun, 24 Aug 2008 23:28:45 +0200 (CEST) firedtv: some header cleanups Unify #ifndef/#define/#endif guards against multiple inclusion. Drop extern keyword from function declarations. Remove #include's into header files where struct declarations suffice. Remove unused ohci1394 interface and related unused ieee1394 interfaces. Add a few missing #include's and remove a few apparently obsolete ones. Sort them alphabetically. Sun, 24 Aug 2008 23:27:45 +0200 (CEST) firedtv: nicer registration message and some initialization fixes Print the correct name in dvb_register_adapter(). While we are at it, replace two switch cascades by one for loop, remove a superfluous member of struct firesat and of two unused arguments of AVCIdentifySubunit(), and fix bogus kfree's in firesat_dvbdev_init(). Tue, 26 Aug 2008 14:24:17 +0200 (CEST) firesat: rename to firedtv Suggested by Andreas Monitzer. Besides DVB-S/-S2 receivers, the driver also supports DVB-C and DVB-T receivers, hence the previous project name is too narrow now. Not yet done: Rename source directory, files, types, variables... Sun, 24 Aug 2008 23:26:23 +0200 (CEST) firesat: add missing copyright notes Reported by Andreas Monitzer and Christian Dolzer. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/ieee1394/dma.h | 1 + drivers/ieee1394/ieee1394_core.c | 1 + drivers/ieee1394/ieee1394_transactions.c | 29 ++++++ drivers/ieee1394/ieee1394_transactions.h | 2 + drivers/ieee1394/iso.h | 1 + drivers/media/dvb/Makefile | 2 +- drivers/media/dvb/firesat/Kconfig | 13 +-- drivers/media/dvb/firesat/Makefile | 4 +- drivers/media/dvb/firesat/avc_api.c | 121 +++++++++-------------- drivers/media/dvb/firesat/avc_api.h | 115 +++++++++++----------- drivers/media/dvb/firesat/cmp.c | 99 ++++++------------- drivers/media/dvb/firesat/cmp.h | 14 +-- drivers/media/dvb/firesat/firesat-ci.c | 16 +-- drivers/media/dvb/firesat/firesat-ci.h | 8 +- drivers/media/dvb/firesat/firesat-rc.c | 147 +++++++++++++++++++++------ drivers/media/dvb/firesat/firesat-rc.h | 13 ++- drivers/media/dvb/firesat/firesat.h | 78 ++++++++------- drivers/media/dvb/firesat/firesat_1394.c | 164 +++++++++++++------------------ drivers/media/dvb/firesat/firesat_dvb.c | 147 +++++++++------------------ drivers/media/dvb/firesat/firesat_fe.c | 41 +++----- drivers/media/dvb/firesat/firesat_iso.c | 12 ++- 21 files changed, 502 insertions(+), 526 deletions(-) (limited to 'drivers') diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h index 2727bcd24194..467373cab8e5 100644 --- a/drivers/ieee1394/dma.h +++ b/drivers/ieee1394/dma.h @@ -12,6 +12,7 @@ #include <asm/types.h> +struct file; struct pci_dev; struct scatterlist; struct vm_area_struct; diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index 2beb8d94f7bd..1028e725a27e 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -1314,6 +1314,7 @@ EXPORT_SYMBOL(hpsb_make_lock64packet); EXPORT_SYMBOL(hpsb_make_phypacket); EXPORT_SYMBOL(hpsb_read); EXPORT_SYMBOL(hpsb_write); +EXPORT_SYMBOL(hpsb_lock); EXPORT_SYMBOL(hpsb_packet_success); /** highlevel.c **/ diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index 10c3d9f8c038..24021d2f0a75 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -570,3 +570,32 @@ int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, return retval; } + +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg) +{ + struct hpsb_packet *packet; + int retval = 0; + + BUG_ON(in_interrupt()); + + packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); + if (!packet) + return -ENOMEM; + + packet->generation = generation; + retval = hpsb_send_packet_and_wait(packet); + if (retval < 0) + goto hpsb_lock_fail; + + retval = hpsb_packet_success(packet); + + if (retval == 0) + *data = packet->data[0]; + +hpsb_lock_fail: + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); + + return retval; +} diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h index d2d5bc3546d7..20b693be14b2 100644 --- a/drivers/ieee1394/ieee1394_transactions.h +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -30,6 +30,8 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length); int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length); +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg); #ifdef HPSB_DEBUG_TLABELS extern spinlock_t hpsb_tlabel_lock; diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h index b5de5f21ef78..c2089c093aa7 100644 --- a/drivers/ieee1394/iso.h +++ b/drivers/ieee1394/iso.h @@ -13,6 +13,7 @@ #define IEEE1394_ISO_H #include <linux/spinlock_types.h> +#include <linux/wait.h> #include <asm/atomic.h> #include <asm/types.h> diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index 41710554012f..cb765816f76c 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -4,4 +4,4 @@ obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ -obj-$(CONFIG_DVB_FIRESAT) += firesat/ +obj-$(CONFIG_DVB_FIREDTV) += firesat/ diff --git a/drivers/media/dvb/firesat/Kconfig b/drivers/media/dvb/firesat/Kconfig index 93f8de5ec3c8..03d25ad10350 100644 --- a/drivers/media/dvb/firesat/Kconfig +++ b/drivers/media/dvb/firesat/Kconfig @@ -1,11 +1,12 @@ -config DVB_FIRESAT - tristate "FireSAT devices" +config DVB_FIREDTV + tristate "FireDTV (FireWire attached DVB receivers)" depends on DVB_CORE && IEEE1394 && INPUT help - Support for external IEEE1394 adapters designed by Digital Everywhere and - produced by El Gato, shipped under the brand name 'FireDTV/FloppyDTV'. + Support for DVB receivers from Digital Everywhere, known as FireDTV + and FloppyDTV, which are connected via IEEE 1394 (FireWire). - These devices don't have a MPEG decoder built in, so you need + These devices don't have an MPEG decoder built in, so you need an external software decoder to watch TV. - Say Y if you own such a device and want to use it. + To compile this driver as a module, say M here: the module will be + called firedtv. diff --git a/drivers/media/dvb/firesat/Makefile b/drivers/media/dvb/firesat/Makefile index be7701b817c9..9e49edc7c49d 100644 --- a/drivers/media/dvb/firesat/Makefile +++ b/drivers/media/dvb/firesat/Makefile @@ -1,4 +1,4 @@ -firesat-objs := firesat_1394.o \ +firedtv-objs := firesat_1394.o \ firesat_dvb.o \ firesat_fe.o \ firesat_iso.o \ @@ -7,7 +7,7 @@ firesat-objs := firesat_1394.o \ firesat-rc.o \ firesat-ci.o -obj-$(CONFIG_DVB_FIRESAT) += firesat.o +obj-$(CONFIG_DVB_FIREDTV) += firedtv.o EXTRA_CFLAGS := -Idrivers/ieee1394 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c index 3c8e7e3dacc2..6337f9f21d0f 100644 --- a/drivers/media/dvb/firesat/avc_api.c +++ b/drivers/media/dvb/firesat/avc_api.c @@ -1,9 +1,9 @@ /* - * FireSAT AVC driver + * FireDTV driver (formerly known as FireSAT) * - * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (c) 2008 Ben Backx <ben@bbackx.com> - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -11,13 +11,20 @@ * the License, or (at your option) any later version. */ -#include "firesat.h" +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <asm/atomic.h> + #include <ieee1394_transactions.h> #include <nodemgr.h> -#include <asm/byteorder.h> -#include <linux/delay.h> -#include <linux/crc32.h> + #include "avc_api.h" +#include "firesat.h" #include "firesat-rc.h" #define RESPONSE_REGISTER 0xFFFFF0000D00ULL @@ -28,8 +35,6 @@ static unsigned int avc_comm_debug = 0; module_param(avc_comm_debug, int, 0644); MODULE_PARM_DESC(avc_comm_debug, "debug logging level [0..2] of AV/C communication, default is 0 (no)"); -static int __AVCRegisterRemoteControl(struct firesat*firesat, int internal); - /* Frees an allocated packet */ static void avc_free_packet(struct hpsb_packet *packet) { @@ -232,67 +237,39 @@ static int __AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, return 0; } -int AVCWrite(struct firesat*firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) { +int AVCWrite(struct firesat*firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) +{ int ret; - if(down_interruptible(&firesat->avc_sem)) + + if (mutex_lock_interruptible(&firesat->avc_mutex)) return -EINTR; ret = __AVCWrite(firesat, CmdFrm, RspFrm); - up(&firesat->avc_sem); + mutex_unlock(&firesat->avc_mutex); return ret; } -static void do_schedule_remotecontrol(unsigned long ignored); -DECLARE_TASKLET(schedule_remotecontrol, do_schedule_remotecontrol, 0); - -static void do_schedule_remotecontrol(unsigned long ignored) { - struct firesat *firesat; - unsigned long flags; - - spin_lock_irqsave(&firesat_list_lock, flags); - list_for_each_entry(firesat,&firesat_list,list) { - if(atomic_read(&firesat->reschedule_remotecontrol) == 1) { - if(down_trylock(&firesat->avc_sem)) - tasklet_schedule(&schedule_remotecontrol); - else { - if(__AVCRegisterRemoteControl(firesat, 1) == 0) - atomic_set(&firesat->reschedule_remotecontrol, 0); - else - tasklet_schedule(&schedule_remotecontrol); - - up(&firesat->avc_sem); - } +int AVCRecv(struct firesat *firesat, u8 *data, size_t length) +{ + AVCRspFrm *RspFrm = (AVCRspFrm *)data; + + if (length >= 8 && + RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && + RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && + RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && + RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { + if (RspFrm->resp == CHANGED) { + firesat_handle_rc(RspFrm->operand[4] << 8 | + RspFrm->operand[5]); + schedule_work(&firesat->remote_ctrl_work); + } else if (RspFrm->resp != INTERIM) { + printk(KERN_INFO "firedtv: remote control result = " + "%d\n", RspFrm->resp); } - } - spin_unlock_irqrestore(&firesat_list_lock, flags); -} - -int AVCRecv(struct firesat *firesat, u8 *data, size_t length) { -// printk(KERN_INFO "%s\n",__func__); - - // remote control handling - -#if 0 - AVCRspFrm *RspFrm = (AVCRspFrm*)data; - - if(/*RspFrm->length >= 8 && ###*/ - ((RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && - RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && - RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2)) && - RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { - if(RspFrm->resp == CHANGED) { -// printk(KERN_INFO "%s: code = %02x %02x\n",__func__,RspFrm->operand[4],RspFrm->operand[5]); - firesat_got_remotecontrolcode((((u16)RspFrm->operand[4]) << 8) | ((u16)RspFrm->operand[5])); - - // schedule - atomic_set(&firesat->reschedule_remotecontrol, 1); - tasklet_schedule(&schedule_remotecontrol); - } else if(RspFrm->resp != INTERIM) - printk(KERN_INFO "%s: remote control result = %d\n",__func__, RspFrm->resp); return 0; } -#endif + if(atomic_read(&firesat->avc_reply_received) == 1) { printk(KERN_ERR "%s: received out-of-order AVC response, " "ignored\n",__func__); @@ -718,7 +695,8 @@ int AVCTuner_GetTS(struct firesat *firesat){ return 0; } -int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport) { +int AVCIdentifySubunit(struct firesat *firesat) +{ AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; @@ -752,8 +730,6 @@ int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *tr printk(KERN_ERR "%s: Invalid response length\n", __func__); return -EINVAL; } - if(systemId) - *systemId = RspFrm.operand[7]; return 0; } @@ -901,7 +877,7 @@ int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount) return 0; } -static int __AVCRegisterRemoteControl(struct firesat*firesat, int internal) +int AVCRegisterRemoteControl(struct firesat *firesat) { AVCCmdFrm CmdFrm; @@ -922,19 +898,16 @@ static int __AVCRegisterRemoteControl(struct firesat*firesat, int internal) CmdFrm.length = 8; - if(internal) { - if(__AVCWrite(firesat,&CmdFrm,NULL) < 0) - return -EIO; - } else - if(AVCWrite(firesat,&CmdFrm,NULL) < 0) - return -EIO; - - return 0; + return AVCWrite(firesat, &CmdFrm, NULL); } -int AVCRegisterRemoteControl(struct firesat*firesat) +void avc_remote_ctrl_work(struct work_struct *work) { - return __AVCRegisterRemoteControl(firesat, 0); + struct firesat *firesat = + container_of(work, struct firesat, remote_ctrl_work); + + /* Should it be rescheduled in failure cases? */ + AVCRegisterRemoteControl(firesat); } int AVCTuner_Host2Ca(struct firesat *firesat) diff --git a/drivers/media/dvb/firesat/avc_api.h b/drivers/media/dvb/firesat/avc_api.h index 041665685903..66f419a6f7c6 100644 --- a/drivers/media/dvb/firesat/avc_api.h +++ b/drivers/media/dvb/firesat/avc_api.h @@ -1,32 +1,25 @@ -/*************************************************************************** - avc_api.h - description - ------------------- - begin : Wed May 1 2000 - copyright : (C) 2000 by Manfred Weihs - copyright : (C) 2003 by Philipp Gutgsell - copyright : (C) 2008 by Henrik Kurelid (henrik@kurelid.se) - email : 0014guph@edu.fh-kaernten.ac.at - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - /* - This is based on code written by Peter Halwachs, - Thomas Groiss and Andreas Monitzer. -*/ - - -#ifndef __AVC_API_H__ -#define __AVC_API_H__ - -#include <linux/dvb/frontend.h> + * AV/C API + * + * Copyright (C) 2000 Manfred Weihs + * Copyright (C) 2003 Philipp Gutgsell <0014guph@edu.fh-kaernten.ac.at> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This is based on code written by Peter Halwachs, Thomas Groiss and + * Andreas Monitzer. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _AVC_API_H +#define _AVC_API_H + +#include <linux/types.h> /************************************************************* Constants from EN510221 @@ -416,34 +409,38 @@ typedef struct #define LNBCONTROL_DONTCARE 0xff - -extern int AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm); -extern int AVCRecv(struct firesat *firesat, u8 *data, size_t length); - -extern int AVCTuner_DSIT(struct firesat *firesat, - int Source_Plug, - struct dvb_frontend_parameters *params, - __u8 *status); - -extern int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_info); -extern int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, __u8 *status); -extern int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]); -extern int AVCTuner_GetTS(struct firesat *firesat); - -extern int AVCIdentifySubunit(struct firesat *firesat, unsigned char *systemId, int *transport); -extern int AVCLNBControl(struct firesat *firesat, char voltage, char burst, char conttone, char nrdiseq, struct dvb_diseqc_master_cmd *diseqcmd); -extern int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount); -extern int AVCRegisterRemoteControl(struct firesat *firesat); -extern int AVCTuner_Host2Ca(struct firesat *firesat); -extern int avc_ca_app_info(struct firesat *firesat, char *app_info, - int *length); -extern int avc_ca_info(struct firesat *firesat, char *app_info, int *length); -extern int avc_ca_reset(struct firesat *firesat); -extern int avc_ca_pmt(struct firesat *firesat, char *app_info, int length); -extern int avc_ca_get_time_date(struct firesat *firesat, int *interval); -extern int avc_ca_enter_menu(struct firesat *firesat); -extern int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, - int *length); - -#endif - +struct dvb_diseqc_master_cmd; +struct dvb_frontend_parameters; +struct firesat; + +int AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, + AVCRspFrm *RspFrm); +int AVCRecv(struct firesat *firesat, u8 *data, size_t length); + +int AVCTuner_DSIT(struct firesat *firesat, int Source_Plug, + struct dvb_frontend_parameters *params, __u8 *status); + +int AVCTunerStatus(struct firesat *firesat, + ANTENNA_INPUT_INFO *antenna_input_info); +int AVCTuner_DSD(struct firesat *firesat, + struct dvb_frontend_parameters *params, __u8 *status); +int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]); +int AVCTuner_GetTS(struct firesat *firesat); + +int AVCIdentifySubunit(struct firesat *firesat); +int AVCLNBControl(struct firesat *firesat, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd); +int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount); +void avc_remote_ctrl_work(struct work_struct *work); +int AVCRegisterRemoteControl(struct firesat *firesat); +int AVCTuner_Host2Ca(struct firesat *firesat); +int avc_ca_app_info(struct firesat *firesat, char *app_info, int *length); +int avc_ca_info(struct firesat *firesat, char *app_info, int *length); +int avc_ca_reset(struct firesat *firesat); +int avc_ca_pmt(struct firesat *firesat, char *app_info, int length); +int avc_ca_get_time_date(struct firesat *firesat, int *interval); +int avc_ca_enter_menu(struct firesat *firesat); +int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, int *length); + +#endif /* _AVC_API_H */ diff --git a/drivers/media/dvb/firesat/cmp.c b/drivers/media/dvb/firesat/cmp.c index a1291caa0674..d1bafba615e4 100644 --- a/drivers/media/dvb/firesat/cmp.c +++ b/drivers/media/dvb/firesat/cmp.c @@ -1,8 +1,8 @@ /* - * FireSAT DVB driver + * FireDTV driver (formerly known as FireSAT) * - * Copyright (c) ? - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -10,15 +10,19 @@ * the License, or (at your option) any later version. */ -#include "cmp.h" -#include <ieee1394.h> -#include <nodemgr.h> -#include <highlevel.h> -#include <ohci1394.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/types.h> + #include <hosts.h> +#include <ieee1394.h> #include <ieee1394_core.h> #include <ieee1394_transactions.h> +#include <nodemgr.h> + #include "avc_api.h" +#include "cmp.h" +#include "firesat.h" typedef struct _OPCR { @@ -38,63 +42,33 @@ typedef struct _OPCR #define FIRESAT_SPEED IEEE1394_SPEED_400 -/* hpsb_lock is being removed from the kernel-source, - * therefor we define our own 'firesat_hpsb_lock'*/ - -int send_packet_and_wait(struct hpsb_packet *packet); - -int firesat_hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, int extcode, quadlet_t * data, quadlet_t arg) { - - struct hpsb_packet *packet; - int retval = 0; - - BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet - - packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); - if (!packet) - return -ENOMEM; - - packet->generation = generation; - retval = send_packet_and_wait(packet); - if (retval < 0) - goto hpsb_lock_fail; - - retval = hpsb_packet_success(packet); - - if (retval == 0) { - *data = packet->data[0]; - } - - hpsb_lock_fail: - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); - - return retval; -} - - -static int cmp_read(struct firesat *firesat, void *buffer, u64 addr, size_t length) { +static int cmp_read(struct firesat *firesat, void *buf, u64 addr, size_t len) +{ int ret; - if(down_interruptible(&firesat->avc_sem)) + + if (mutex_lock_interruptible(&firesat->avc_mutex)) return -EINTR; - ret = hpsb_read(firesat->host, firesat->nodeentry->nodeid, firesat->nodeentry->generation, - addr, buffer, length); + ret = hpsb_read(firesat->host, firesat->nodeentry->nodeid, + firesat->nodeentry->generation, addr, buf, len); - up(&firesat->avc_sem); + mutex_unlock(&firesat->avc_mutex); return ret; } -static int cmp_lock(struct firesat *firesat, quadlet_t *data, u64 addr, quadlet_t arg, int ext_tcode) { +static int cmp_lock(struct firesat *firesat, quadlet_t *data, u64 addr, + quadlet_t arg, int ext_tcode) +{ int ret; - if(down_interruptible(&firesat->avc_sem)) + + if (mutex_lock_interruptible(&firesat->avc_mutex)) return -EINTR; - ret = firesat_hpsb_lock(firesat->host, firesat->nodeentry->nodeid, firesat->nodeentry->generation, - addr, ext_tcode, data, arg); + ret = hpsb_lock(firesat->host, firesat->nodeentry->nodeid, + firesat->nodeentry->generation, + addr, ext_tcode, data, arg); - up(&firesat->avc_sem); + mutex_unlock(&firesat->avc_mutex); return ret; } @@ -223,20 +197,3 @@ int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug,int iso_ch } return 0; } - -static void complete_packet(void *data) { - complete((struct completion *) data); -} - -int send_packet_and_wait(struct hpsb_packet *packet) { - struct completion done; - int retval; - - init_completion(&done); - hpsb_set_packet_complete_task(packet, complete_packet, &done); - retval = hpsb_send_packet(packet); - if (retval == 0) - wait_for_completion(&done); - - return retval; -} diff --git a/drivers/media/dvb/firesat/cmp.h b/drivers/media/dvb/firesat/cmp.h index d43fbc29f262..600d5784dc72 100644 --- a/drivers/media/dvb/firesat/cmp.h +++ b/drivers/media/dvb/firesat/cmp.h @@ -1,9 +1,11 @@ -#ifndef __FIRESAT__CMP_H_ -#define __FIRESAT__CMP_H_ +#ifndef _CMP_H +#define _CMP_H -#include "firesat.h" +struct firesat; -extern int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, int iso_channel); -extern int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug,int iso_channel); +int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, + int iso_channel); +int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug, + int iso_channel); -#endif +#endif /* _CMP_H */ diff --git a/drivers/media/dvb/firesat/firesat-ci.c b/drivers/media/dvb/firesat/firesat-ci.c index 821048db283b..3ef25cc4bfdb 100644 --- a/drivers/media/dvb/firesat/firesat-ci.c +++ b/drivers/media/dvb/firesat/firesat-ci.c @@ -1,7 +1,8 @@ /* - * FireSAT DVB driver + * FireDTV driver (formerly known as FireSAT) * - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,13 +10,16 @@ * the License, or (at your option) any later version. */ -#include "firesat-ci.h" -#include "firesat.h" -#include "avc_api.h" - #include <linux/dvb/ca.h> +#include <linux/fs.h> +#include <linux/module.h> + #include <dvbdev.h> +#include "avc_api.h" +#include "firesat.h" +#include "firesat-ci.h" + static unsigned int ca_debug = 0; module_param(ca_debug, int, 0644); MODULE_PARM_DESC(ca_debug, "debug logging of ca system, default is 0 (no)"); diff --git a/drivers/media/dvb/firesat/firesat-ci.h b/drivers/media/dvb/firesat/firesat-ci.h index dafe3f0f0cc9..04fe4061c778 100644 --- a/drivers/media/dvb/firesat/firesat-ci.h +++ b/drivers/media/dvb/firesat/firesat-ci.h @@ -1,9 +1,9 @@ -#ifndef __FIRESAT_CA_H -#define __FIRESAT_CA_H +#ifndef _FIREDTV_CI_H +#define _FIREDTV_CI_H -#include "firesat.h" +struct firesat; int firesat_ca_init(struct firesat *firesat); void firesat_ca_release(struct firesat *firesat); -#endif +#endif /* _FIREDTV_CI_H */ diff --git a/drivers/media/dvb/firesat/firesat-rc.c b/drivers/media/dvb/firesat/firesat-rc.c index e300b81008af..d3e08f9fe9f7 100644 --- a/drivers/media/dvb/firesat/firesat-rc.c +++ b/drivers/media/dvb/firesat/firesat-rc.c @@ -1,9 +1,26 @@ -#include "firesat.h" -#include "firesat-rc.h" +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#include <linux/bitops.h> #include <linux/input.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "firesat-rc.h" + +/* fixed table with older keycodes, geared towards MythTV */ +const static u16 oldtable[] = { + + /* code from device: 0x4501...0x451f */ -static u16 firesat_irtable[] = { KEY_ESC, KEY_F9, KEY_1, @@ -35,50 +52,124 @@ static u16 firesat_irtable[] = { KEY_RIGHT, KEY_P, KEY_M, + + /* code from device: 0x4540...0x4542 */ + KEY_R, KEY_V, KEY_C, - 0 }; -static struct input_dev firesat_idev; +/* user-modifiable table for a remote as sold in 2008 */ +static u16 keytable[] = { + + /* code from device: 0x0300...0x031f */ + + [0x00] = KEY_POWER, + [0x01] = KEY_SLEEP, + [0x02] = KEY_STOP, + [0x03] = KEY_OK, + [0x04] = KEY_RIGHT, + [0x05] = KEY_1, + [0x06] = KEY_2, + [0x07] = KEY_3, + [0x08] = KEY_LEFT, + [0x09] = KEY_4, + [0x0a] = KEY_5, + [0x0b] = KEY_6, + [0x0c] = KEY_UP, + [0x0d] = KEY_7, + [0x0e] = KEY_8, + [0x0f] = KEY_9, + [0x10] = KEY_DOWN, + [0x11] = KEY_TITLE, /* "OSD" - fixme */ + [0x12] = KEY_0, + [0x13] = KEY_F20, /* "16:9" - fixme */ + [0x14] = KEY_SCREEN, /* "FULL" - fixme */ + [0x15] = KEY_MUTE, + [0x16] = KEY_SUBTITLE, + [0x17] = KEY_RECORD, + [0x18] = KEY_TEXT, + [0x19] = KEY_AUDIO, + [0x1a] = KEY_RED, + [0x1b] = KEY_PREVIOUS, + [0x1c] = KEY_REWIND, + [0x1d] = KEY_PLAYPAUSE, + [0x1e] = KEY_NEXT, + [0x1f] = KEY_VOLUMEUP, + + /* code from device: 0x0340...0x0354 */ + + [0x20] = KEY_CHANNELUP, + [0x21] = KEY_F21, /* "4:3" - fixme */ + [0x22] = KEY_TV, + [0x23] = KEY_DVD, + [0x24] = KEY_VCR, + [0x25] = KEY_AUX, + [0x26] = KEY_GREEN, + [0x27] = KEY_YELLOW, + [0x28] = KEY_BLUE, + [0x29] = KEY_CHANNEL, /* "CH.LIST" */ + [0x2a] = KEY_VENDOR, /* "CI" - fixme */ + [0x2b] = KEY_VOLUMEDOWN, + [0x2c] = KEY_CHANNELDOWN, + [0x2d] = KEY_LAST, + [0x2e] = KEY_INFO, + [0x2f] = KEY_FORWARD, + [0x30] = KEY_LIST, + [0x31] = KEY_FAVORITES, + [0x32] = KEY_MENU, + [0x33] = KEY_EPG, + [0x34] = KEY_EXIT, +}; + +static struct input_dev *idev; int firesat_register_rc(void) { - int index; + int i, err; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; - memset(&firesat_idev, 0, sizeof(firesat_idev)); + idev->name = "FireDTV remote control"; + idev->evbit[0] = BIT_MASK(EV_KEY); + idev->keycode = keytable; + idev->keycodesize = sizeof(keytable[0]); + idev->keycodemax = ARRAY_SIZE(keytable); - firesat_idev.evbit[0] = BIT(EV_KEY); + for (i = 0; i < ARRAY_SIZE(keytable); i++) + set_bit(keytable[i], idev->keybit); - for (index = 0; firesat_irtable[index] != 0; index++) - set_bit(firesat_irtable[index], firesat_idev.keybit); + err = input_register_device(idev); + if (err) + input_free_device(idev); - return input_register_device(&firesat_idev); + return err; } -int firesat_unregister_rc(void) +void firesat_unregister_rc(void) { - input_unregister_device(&firesat_idev); - return 0; + input_unregister_device(idev); } -int firesat_got_remotecontrolcode(u16 code) +void firesat_handle_rc(unsigned int code) { - u16 keycode; - - if (code > 0x4500 && code < 0x4520) - keycode = firesat_irtable[code - 0x4501]; - else if (code > 0x453f && code < 0x4543) - keycode = firesat_irtable[code - 0x4521]; + if (code >= 0x0300 && code <= 0x031f) + code = keytable[code - 0x0300]; + else if (code >= 0x0340 && code <= 0x0354) + code = keytable[code - 0x0320]; + else if (code >= 0x4501 && code <= 0x451f) + code = oldtable[code - 0x4501]; + else if (code >= 0x4540 && code <= 0x4542) + code = oldtable[code - 0x4521]; else { - printk(KERN_DEBUG "%s: invalid key code 0x%04x\n", __func__, - code); - return -EINVAL; + printk(KERN_DEBUG "firedtv: invalid key code 0x%04x " + "from remote control\n", code); + return; } - input_report_key(&firesat_idev, keycode, 1); - input_report_key(&firesat_idev, keycode, 0); - - return 0; + input_report_key(idev, code, 1); + input_report_key(idev, code, 0); } diff --git a/drivers/media/dvb/firesat/firesat-rc.h b/drivers/media/dvb/firesat/firesat-rc.h index e89a8069ba88..81f4fdec60f1 100644 --- a/drivers/media/dvb/firesat/firesat-rc.h +++ b/drivers/media/dvb/firesat/firesat-rc.h @@ -1,9 +1,8 @@ -#ifndef __FIRESAT_LIRC_H -#define __FIRESAT_LIRC_H +#ifndef _FIREDTV_RC_H +#define _FIREDTV_RC_H -extern int firesat_register_rc(void); -extern int firesat_unregister_rc(void); -extern int firesat_got_remotecontrolcode(u16 code); - -#endif +int firesat_register_rc(void); +void firesat_unregister_rc(void); +void firesat_handle_rc(unsigned int code); +#endif /* _FIREDTV_RC_H */ diff --git a/drivers/media/dvb/firesat/firesat.h b/drivers/media/dvb/firesat/firesat.h index f0bac244783a..5f0de88e41a6 100644 --- a/drivers/media/dvb/firesat/firesat.h +++ b/drivers/media/dvb/firesat/firesat.h @@ -1,8 +1,8 @@ /* - * FireSAT DVB driver + * FireDTV driver (formerly known as FireSAT) * - * Copyright (c) ? - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -10,22 +10,26 @@ * the License, or (at your option) any later version. */ -#ifndef __FIRESAT_H -#define __FIRESAT_H +#ifndef _FIREDTV_H +#define _FIREDTV_H -#include "dvb_frontend.h" -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_net.h" - -#include <linux/version.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) -#include <linux/semaphore.h> -#endif -#include <linux/dvb/frontend.h> #include <linux/dvb/dmx.h> -#include <iso.h> +#include <linux/dvb/frontend.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <asm/atomic.h> + +#include <demux.h> +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvb_net.h> +#include <dvbdev.h> +#include <linux/version.h> #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) #define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w, v) #else @@ -116,15 +120,19 @@ enum model_type { - FireSAT_DVB_S = 1, - FireSAT_DVB_C = 2, - FireSAT_DVB_T = 3, - FireSAT_DVB_S2 = 4 + FireSAT_UNKNOWN = 0, + FireSAT_DVB_S = 1, + FireSAT_DVB_C = 2, + FireSAT_DVB_T = 3, + FireSAT_DVB_S2 = 4, }; +struct hpsb_host; +struct hpsb_iso; +struct node_entry; + struct firesat { struct dvb_demux dvb_demux; - char *model_name; /* DVB bits */ struct dvb_adapter *adapter; @@ -139,11 +147,10 @@ struct firesat { int ca_last_command; int ca_time_interval; - struct semaphore avc_sem; + struct mutex avc_mutex; wait_queue_head_t avc_wait; atomic_t avc_reply_received; - - atomic_t reschedule_remotecontrol; + struct work_struct remote_ctrl_work; struct firesat_channel { struct firesat *firesat; @@ -154,7 +161,7 @@ struct firesat { int pid; int type; /* 1 - TS, 2 - Filter */ } channel[16]; - struct semaphore demux_sem; + struct mutex demux_mutex; /* needed by avc_api */ void *respfrm; @@ -210,22 +217,23 @@ struct CIPHeader { }; }; +extern const char *firedtv_model_names[]; extern struct list_head firesat_list; extern spinlock_t firesat_list_lock; +struct device; + /* firesat_dvb.c */ -extern int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed); -extern int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed); -extern int firesat_dvbdev_init(struct firesat *firesat, - struct device *dev, - struct dvb_frontend *fe); +int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed); +int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +int firesat_dvbdev_init(struct firesat *firesat, struct device *dev, + struct dvb_frontend *fe); /* firesat_fe.c */ -extern int firesat_frontend_attach(struct firesat *firesat, - struct dvb_frontend *fe); +int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe); /* firesat_iso.c */ -extern int setup_iso_channel(struct firesat *firesat); -extern void tear_down_iso_channel(struct firesat *firesat); +int setup_iso_channel(struct firesat *firesat); +void tear_down_iso_channel(struct firesat *firesat); -#endif +#endif /* _FIREDTV_H */ diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c index b19e59416b59..a13fbe6b3a3c 100644 --- a/drivers/media/dvb/firesat/firesat_1394.c +++ b/drivers/media/dvb/firesat/firesat_1394.c @@ -1,9 +1,9 @@ /* - * FireSAT DVB driver + * FireDTV driver (formerly known as FireSAT) * - * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (c) 2007-2008 Ben Backx <ben@bbackx.com> - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -11,26 +11,34 @@ * the License, or (at your option) any later version. */ -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/wait.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/time.h> +#include <linux/device.h> #include <linux/errno.h> -#include <linux/interrupt.h> -#include <ieee1394_hotplug.h> -#include <nodemgr.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <asm/atomic.h> + +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> +#include <dvbdev.h> + +#include <csr1212.h> #include <highlevel.h> -#include <ohci1394.h> #include <hosts.h> -#include <dvbdev.h> +#include <ieee1394_hotplug.h> +#include <nodemgr.h> -#include "firesat.h" #include "avc_api.h" #include "cmp.h" -#include "firesat-rc.h" +#include "firesat.h" #include "firesat-ci.h" +#include "firesat-rc.h" #define FIRESAT_Vendor_ID 0x001287 @@ -75,52 +83,6 @@ MODULE_DEVICE_TABLE(ieee1394, firesat_id_table); LIST_HEAD(firesat_list); spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED; -static void firesat_add_host(struct hpsb_host *host); -static void firesat_remove_host(struct hpsb_host *host); -static void firesat_host_reset(struct hpsb_host *host); - -static void fcp_request(struct hpsb_host *host, - int nodeid, - int direction, - int cts, - u8 *data, - size_t length); - -static struct hpsb_highlevel firesat_highlevel = { - .name = "FireSAT", - .add_host = firesat_add_host, - .remove_host = firesat_remove_host, - .host_reset = firesat_host_reset, - .fcp_request = fcp_request, -}; - -static void firesat_add_host (struct hpsb_host *host) -{ - struct ti_ohci *ohci = (struct ti_ohci *)host->hostdata; - - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - if (!hpsb_create_hostinfo(&firesat_highlevel, host, 0)) { - printk(KERN_ERR "Cannot allocate hostinfo\n"); - return; - } - - hpsb_set_hostinfo(&firesat_highlevel, host, ohci); - hpsb_set_hostinfo_key(&firesat_highlevel, host, ohci->host->id); -} - -static void firesat_remove_host (struct hpsb_host *host) -{ - -} - -static void firesat_host_reset(struct hpsb_host *host) -{ - printk(KERN_INFO "FireSAT host_reset (nodeid = 0x%x, hosts active = %d)\n",host->node_id,host->nodes_active); -} - static void fcp_request(struct hpsb_host *host, int nodeid, int direction, @@ -156,6 +118,14 @@ static void fcp_request(struct hpsb_host *host, printk("%s: received invalid fcp request, ignored\n", __func__); } +const char *firedtv_model_names[] = { + [FireSAT_UNKNOWN] = "unknown type", + [FireSAT_DVB_S] = "FireDTV S/CI", + [FireSAT_DVB_C] = "FireDTV C/CI", + [FireSAT_DVB_T] = "FireDTV T/CI", + [FireSAT_DVB_S2] = "FireDTV S2 ", +}; + static int firesat_probe(struct device *dev) { struct unit_directory *ud = container_of(dev, struct unit_directory, device); @@ -165,6 +135,7 @@ static int firesat_probe(struct device *dev) unsigned char subunitcount = 0xff, subunit; struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL); int kv_len; + int i; char *kv_buf; if (!firesats) { @@ -207,11 +178,11 @@ static int firesat_probe(struct device *dev) return -ENOMEM; } - sema_init(&firesat->avc_sem, 1); + mutex_init(&firesat->avc_mutex); init_waitqueue_head(&firesat->avc_wait); atomic_set(&firesat->avc_reply_received, 1); - sema_init(&firesat->demux_sem, 1); - atomic_set(&firesat->reschedule_remotecontrol, 0); + mutex_init(&firesat->demux_mutex); + INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work); spin_lock_irqsave(&firesat_list_lock, flags); INIT_LIST_HEAD(&firesat->list); @@ -244,23 +215,13 @@ static int firesat_probe(struct device *dev) while ((kv_buf + kv_len - 1) == '\0') kv_len--; kv_buf[kv_len++] = '\0'; - /* Determining the device model */ - if (strcmp(kv_buf, "FireDTV S/CI") == 0) { - printk(KERN_INFO "%s: found DVB/S\n", __func__); - firesat->type = 1; - } else if (strcmp(kv_buf, "FireDTV C/CI") == 0) { - printk(KERN_INFO "%s: found DVB/C\n", __func__); - firesat->type = 2; - } else if (strcmp(kv_buf, "FireDTV T/CI") == 0) { - printk(KERN_INFO "%s: found DVB/T\n", __func__); - firesat->type = 3; - } else if (strcmp(kv_buf, "FireDTV S2 ") == 0) { - printk(KERN_INFO "%s: found DVB/S2\n", __func__); - firesat->type = 4; - } + for (i = ARRAY_SIZE(firedtv_model_names); --i;) + if (strcmp(kv_buf, firedtv_model_names[i]) == 0) + break; + firesat->type = i; kfree(kv_buf); - if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type)) { + if (AVCIdentifySubunit(firesat)) { printk("%s: cannot identify subunit %d\n", __func__, subunit); spin_lock_irqsave(&firesat_list_lock, flags); list_del(&firesat->list); @@ -270,14 +231,14 @@ static int firesat_probe(struct device *dev) } // ---- + /* FIXME: check for error return */ firesat_dvbdev_init(firesat, dev, fe); // ---- firesats[subunit] = firesat; } // loop for all tuners - //beta ;-) Disable remote control stuff to avoid crashing - //if(firesats[0]) - // AVCRegisterRemoteControl(firesats[0]); + if (firesats[0]) + AVCRegisterRemoteControl(firesats[0]); return 0; } @@ -306,6 +267,8 @@ static int firesat_remove(struct device *dev) list_del(&firesats[k]->list); spin_unlock_irqrestore(&firesat_list_lock, flags); + cancel_work_sync(&firesats[k]->remote_ctrl_work); + kfree(firesats[k]->fe); kfree(firesats[k]->adapter); kfree(firesats[k]->respfrm); @@ -339,7 +302,7 @@ static int firesat_update(struct unit_directory *ud) static struct hpsb_protocol_driver firesat_driver = { - .name = "FireSAT", + .name = "firedtv", .id_table = firesat_id_table, .update = firesat_update, @@ -352,32 +315,41 @@ static struct hpsb_protocol_driver firesat_driver = { }, }; +static struct hpsb_highlevel firesat_highlevel = { + .name = "firedtv", + .fcp_request = fcp_request, +}; + static int __init firesat_init(void) { int ret; - printk(KERN_INFO "FireSAT loaded\n"); hpsb_register_highlevel(&firesat_highlevel); ret = hpsb_register_protocol(&firesat_driver); if (ret) { - printk(KERN_ERR "FireSAT: failed to register protocol\n"); - hpsb_unregister_highlevel(&firesat_highlevel); - return ret; + printk(KERN_ERR "firedtv: failed to register protocol\n"); + goto fail; } - //Crash in this function, just disable RC for the time being... - //Don't forget to uncomment in firesat_exit and firesat_probe when you enable this. - /*if((ret=firesat_register_rc())) - printk("%s: firesat_register_rc return error code %d (ignored)\n", __func__, ret);*/ + ret = firesat_register_rc(); + if (ret) { + printk(KERN_ERR "firedtv: failed to register input device\n"); + goto fail_rc; + } return 0; +fail_rc: + hpsb_unregister_protocol(&firesat_driver); +fail: + hpsb_unregister_highlevel(&firesat_highlevel); + return ret; } static void __exit firesat_exit(void) { + firesat_unregister_rc(); hpsb_unregister_protocol(&firesat_driver); hpsb_unregister_highlevel(&firesat_highlevel); - printk(KERN_INFO "FireSAT quit\n"); } module_init(firesat_init); @@ -385,6 +357,6 @@ module_exit(firesat_exit); MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); -MODULE_DESCRIPTION("FireSAT DVB Driver"); +MODULE_DESCRIPTION("FireDTV DVB Driver"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("FireSAT DVB"); +MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firesat/firesat_dvb.c b/drivers/media/dvb/firesat/firesat_dvb.c index 9e87402289a6..e944cee19f0a 100644 --- a/drivers/media/dvb/firesat/firesat_dvb.c +++ b/drivers/media/dvb/firesat/firesat_dvb.c @@ -1,8 +1,8 @@ /* - * FireSAT DVB driver + * FireDTV driver (formerly known as FireSAT) * - * Copyright (c) ? - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -10,64 +10,52 @@ * the License, or (at your option) any later version. */ -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/wait.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/time.h> #include <linux/errno.h> -#include <linux/interrupt.h> -#include <ieee1394_hotplug.h> -#include <nodemgr.h> -#include <highlevel.h> -#include <ohci1394.h> -#include <hosts.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/types.h> + +#include <dvb_demux.h> +#include <dvb_frontend.h> #include <dvbdev.h> -#include "firesat.h" #include "avc_api.h" -#include "cmp.h" -#include "firesat-rc.h" +#include "firesat.h" #include "firesat-ci.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static struct firesat_channel *firesat_channel_allocate(struct firesat *firesat) { + struct firesat_channel *c = NULL; int k; - //printk(KERN_INFO "%s\n", __func__); - - if (down_interruptible(&firesat->demux_sem)) + if (mutex_lock_interruptible(&firesat->demux_mutex)) return NULL; - for (k = 0; k < 16; k++) { - //printk(KERN_INFO "%s: channel %d: active = %d, pid = 0x%x\n",__func__,k,firesat->channel[k].active,firesat->channel[k].pid); - + for (k = 0; k < 16; k++) if (firesat->channel[k].active == 0) { firesat->channel[k].active = 1; - up(&firesat->demux_sem); - return &firesat->channel[k]; + c = &firesat->channel[k]; + break; } - } - up(&firesat->demux_sem); - return NULL; // no more channels available + mutex_unlock(&firesat->demux_mutex); + return c; } static int firesat_channel_collect(struct firesat *firesat, int *pidc, u16 pid[]) { int k, l = 0; - if (down_interruptible(&firesat->demux_sem)) + if (mutex_lock_interruptible(&firesat->demux_mutex)) return -EINTR; for (k = 0; k < 16; k++) if (firesat->channel[k].active == 1) pid[l++] = firesat->channel[k].pid; - up(&firesat->demux_sem); + mutex_unlock(&firesat->demux_mutex); *pidc = l; @@ -77,12 +65,12 @@ static int firesat_channel_collect(struct firesat *firesat, int *pidc, u16 pid[] static int firesat_channel_release(struct firesat *firesat, struct firesat_channel *channel) { - if (down_interruptible(&firesat->demux_sem)) + if (mutex_lock_interruptible(&firesat->demux_mutex)) return -EINTR; channel->active = 0; - up(&firesat->demux_sem); + mutex_unlock(&firesat->demux_mutex); return 0; } @@ -172,7 +160,8 @@ int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *demux = dvbdmxfeed->demux; struct firesat *firesat = (struct firesat*)demux->priv; - int k, l = 0; + struct firesat_channel *c = dvbdmxfeed->priv; + int k, l; u16 pids[16]; //printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid); @@ -197,30 +186,24 @@ int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) return 0; } - if (down_interruptible(&firesat->demux_sem)) + if (mutex_lock_interruptible(&firesat->demux_mutex)) return -EINTR; - - // list except channel to be removed - for (k = 0; k < 16; k++) + /* list except channel to be removed */ + for (k = 0, l = 0; k < 16; k++) if (firesat->channel[k].active == 1) { - if (&firesat->channel[k] != - (struct firesat_channel *)dvbdmxfeed->priv) + if (&firesat->channel[k] != c) pids[l++] = firesat->channel[k].pid; else firesat->channel[k].active = 0; } - if ((k = AVCTuner_SetPIDs(firesat, l, pids))) { - up(&firesat->demux_sem); - return k; - } + k = AVCTuner_SetPIDs(firesat, l, pids); + if (!k) + c->active = 0; - ((struct firesat_channel *)dvbdmxfeed->priv)->active = 0; - - up(&firesat->demux_sem); - - return 0; + mutex_unlock(&firesat->demux_mutex); + return k; } int firesat_dvbdev_init(struct firesat *firesat, @@ -229,60 +212,20 @@ int firesat_dvbdev_init(struct firesat *firesat, { int result; -#if 0 - switch (firesat->type) { - case FireSAT_DVB_S: - firesat->model_name = "FireSAT DVB-S"; - firesat->frontend_info = &firesat_S_frontend_info; - break; - case FireSAT_DVB_C: - firesat->model_name = "FireSAT DVB-C"; - firesat->frontend_info = &firesat_C_frontend_info; - break; - case FireSAT_DVB_T: - firesat->model_name = "FireSAT DVB-T"; - firesat->frontend_info = &firesat_T_frontend_info; - break; - default: - printk("%s: unknown model type 0x%x on subunit %d!\n", - __func__, firesat->type,subunit); - firesat->model_name = "Unknown"; - firesat->frontend_info = NULL; - } -#endif -/* // ------- CRAP ----------- - if (!firesat->frontend_info) { - spin_lock_irqsave(&firesat_list_lock, flags); - list_del(&firesat->list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - kfree(firesat); - continue; - } -*/ - //initialising firesat->adapter before calling dvb_register_adapter - if (!(firesat->adapter = kmalloc(sizeof (struct dvb_adapter), GFP_KERNEL))) { - printk("%s: couldn't allocate memory.\n", __func__); - kfree(firesat->adapter); - kfree(firesat); - return -ENOMEM; - } - - if ((result = DVB_REGISTER_ADAPTER(firesat->adapter, - firesat->model_name, - THIS_MODULE, - dev, adapter_nr)) < 0) { - - printk("%s: dvb_register_adapter failed: error %d\n", __func__, result); -#if 0 - /* ### cleanup */ - spin_lock_irqsave(&firesat_list_lock, flags); - list_del(&firesat->list); - spin_unlock_irqrestore(&firesat_list_lock, flags); -#endif - kfree(firesat); + firesat->adapter = kmalloc(sizeof(*firesat->adapter), GFP_KERNEL); + if (!firesat->adapter) { + printk(KERN_ERR "firedtv: couldn't allocate memory\n"); + return -ENOMEM; + } - return result; - } + result = DVB_REGISTER_ADAPTER(firesat->adapter, + firedtv_model_names[firesat->type], + THIS_MODULE, dev, adapter_nr); + if (result < 0) { + printk(KERN_ERR "firedtv: dvb_register_adapter failed\n"); + kfree(firesat->adapter); + return result; + } memset(&firesat->demux, 0, sizeof(struct dvb_demux)); firesat->demux.dmx.capabilities = 0/*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/; diff --git a/drivers/media/dvb/firesat/firesat_fe.c b/drivers/media/dvb/firesat/firesat_fe.c index 1c86c3e61373..ec614ea8de22 100644 --- a/drivers/media/dvb/firesat/firesat_fe.c +++ b/drivers/media/dvb/firesat/firesat_fe.c @@ -1,8 +1,8 @@ /* - * FireSAT DVB driver + * FireDTV driver (formerly known as FireSAT) * - * Copyright (c) ? - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -10,26 +10,15 @@ * the License, or (at your option) any later version. */ -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/wait.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/time.h> #include <linux/errno.h> -#include <linux/interrupt.h> -#include <ieee1394_hotplug.h> -#include <nodemgr.h> -#include <highlevel.h> -#include <ohci1394.h> -#include <hosts.h> -#include <dvbdev.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <dvb_frontend.h> -#include "firesat.h" #include "avc_api.h" #include "cmp.h" -#include "firesat-rc.h" -#include "firesat-ci.h" +#include "firesat.h" static int firesat_dvb_init(struct dvb_frontend *fe) { @@ -209,21 +198,17 @@ int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe) { switch (firesat->type) { case FireSAT_DVB_S: - firesat->model_name = "FireSAT DVB-S"; firesat->frontend_info = &firesat_S_frontend_info; break; case FireSAT_DVB_C: - firesat->model_name = "FireSAT DVB-C"; firesat->frontend_info = &firesat_C_frontend_info; break; case FireSAT_DVB_T: - firesat->model_name = "FireSAT DVB-T"; firesat->frontend_info = &firesat_T_frontend_info; break; default: - printk("%s: unknown model type 0x%x !\n", - __func__, firesat->type); - firesat->model_name = "Unknown"; + printk(KERN_ERR "firedtv: no frontend for model type 0x%x\n", + firesat->type); firesat->frontend_info = NULL; } fe->ops = firesat_ops; @@ -235,7 +220,7 @@ int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe) static struct dvb_frontend_info firesat_S_frontend_info = { - .name = "FireSAT DVB-S Frontend", + .name = "FireDTV DVB-S Frontend", .type = FE_QPSK, .frequency_min = 950000, @@ -256,7 +241,7 @@ static struct dvb_frontend_info firesat_S_frontend_info = { static struct dvb_frontend_info firesat_C_frontend_info = { - .name = "FireSAT DVB-C Frontend", + .name = "FireDTV DVB-C Frontend", .type = FE_QAM, .frequency_min = 47000000, @@ -276,7 +261,7 @@ static struct dvb_frontend_info firesat_C_frontend_info = { static struct dvb_frontend_info firesat_T_frontend_info = { - .name = "FireSAT DVB-T Frontend", + .name = "FireDTV DVB-T Frontend", .type = FE_OFDM, .frequency_min = 49000000, diff --git a/drivers/media/dvb/firesat/firesat_iso.c b/drivers/media/dvb/firesat/firesat_iso.c index 15e23cf7d503..bc94afe57f63 100644 --- a/drivers/media/dvb/firesat/firesat_iso.c +++ b/drivers/media/dvb/firesat/firesat_iso.c @@ -1,7 +1,7 @@ /* * FireSAT DVB driver * - * Copyright (c) 2008 Henrik Kurelid <henrik@kurelid.se> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,6 +9,16 @@ * the License, or (at your option) any later version. */ +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/spinlock.h> + +#include <dvb_demux.h> + +#include <dma.h> +#include <iso.h> + #include "firesat.h" static void rawiso_activity_cb(struct hpsb_iso *iso); -- cgit v1.2.3 From 29f8ea8ab09bad0c3c0d67964559d27643e97903 Mon Sep 17 00:00:00 2001 From: Stefan Richter <stefanr@s5r6.in-berlin.de> Date: Wed, 27 Aug 2008 01:18:32 +0200 Subject: ieee1394: use correct barrier types between accesses of nodeid and generation A compiler barrier (explicit on the read side, implicit on the write side) is not quite enough for what has to be accomplished here. Use hardware memory barriers on systems which need them. (Of course a full fix of generation handling would require much more than this. The ieee1394 core's bus generation counter had to be tied to the controller's bus generation counter; cf. Kristian's stack. It's just that I have other current business with the code around these barrier()s, so why not do at least this small fix.) Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/ieee1394/nodemgr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 906c5a98d814..f47b6f69d2a9 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -1265,7 +1265,8 @@ static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr, csr1212_destroy_csr(csr); } - /* Mark the node current */ + /* Finally, mark the node current */ + smp_wmb(); ne->generation = generation; if (ne->in_limbo) { @@ -1798,7 +1799,7 @@ void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet) { packet->host = ne->host; packet->generation = ne->generation; - barrier(); + smp_rmb(); packet->node_id = ne->nodeid; } @@ -1807,7 +1808,7 @@ int hpsb_node_write(struct node_entry *ne, u64 addr, { unsigned int generation = ne->generation; - barrier(); + smp_rmb(); return hpsb_write(ne->host, ne->nodeid, generation, addr, buffer, length); } -- cgit v1.2.3 From b33fdd6ca576d6c476c6aebf350d4556294d74ac Mon Sep 17 00:00:00 2001 From: Stefan Richter <stefanr@s5r6.in-berlin.de> Date: Wed, 27 Aug 2008 13:40:02 +0200 Subject: ieee1394: add hpsb_node_read() and hpsb_node_lock() These will be used by the firedtv driver. Like hpsb_node_write() they are much better APIs for high-level drivers than hpsb_write() and its siblings --- easier to use correctly and also terser. Unlike hspb_node_write(), the two new functions will only be used by one call site. Hence make them static inline instead of exported symbols. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/ieee1394/nodemgr.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h index 15ea09733e84..ee5acdbd114a 100644 --- a/drivers/ieee1394/nodemgr.h +++ b/drivers/ieee1394/nodemgr.h @@ -21,9 +21,11 @@ #define _IEEE1394_NODEMGR_H #include <linux/device.h> +#include <asm/system.h> #include <asm/types.h> #include "ieee1394_core.h" +#include "ieee1394_transactions.h" #include "ieee1394_types.h" struct csr1212_csr; @@ -154,6 +156,22 @@ static inline int hpsb_node_entry_valid(struct node_entry *ne) void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet); int hpsb_node_write(struct node_entry *ne, u64 addr, quadlet_t *buffer, size_t length); +static inline int hpsb_node_read(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length) +{ + unsigned int g = ne->generation; + + smp_rmb(); + return hpsb_read(ne->host, ne->nodeid, g, addr, buffer, length); +} +static inline int hpsb_node_lock(struct node_entry *ne, u64 addr, int extcode, + quadlet_t *buffer, quadlet_t arg) +{ + unsigned int g = ne->generation; + + smp_rmb(); + return hpsb_lock(ne->host, ne->nodeid, g, addr, extcode, buffer, arg); +} int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)); int init_ieee1394_nodemgr(void); -- cgit v1.2.3 From 9c939e4df432fe4ed17bdbf7bc14111ec51ef7c9 Mon Sep 17 00:00:00 2001 From: Stefan Richter <stefanr@s5r6.in-berlin.de> Date: Wed, 27 Aug 2008 01:24:25 +0200 Subject: ieee1394: inherit ud vendor_id from node vendor_id While Module_Vendor_ID in the configuration ROM's root directory is mandatory, there often aren't vendor IDs in unit directories. This affects the new firedtv driver which is meant to be auto-loaded and matched only for vendor-specific devices. We now always copy ne->vendor_id into ud->vendor_id before we scan a unit directory (and fill in a possibly present vendor ID from there). This way, the root directory's vendor ID is used as fallback in the "uevent" environment for modprobe'ing per module alias when a node was plugged in, and in the driver match routine when protocol drivers are bound to unit directories. It will however not be used as sysfs attribute of a unit directory device. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/ieee1394/nodemgr.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index f47b6f69d2a9..53aada5bbe1e 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -971,6 +971,9 @@ static struct unit_directory *nodemgr_process_unit_directory ud->ud_kv = ud_kv; ud->id = (*id)++; + /* inherit vendor_id from root directory if none exists in unit dir */ + ud->vendor_id = ne->vendor_id; + csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) { switch (kv->key.id) { case CSR1212_KV_ID_VENDOR: -- cgit v1.2.3 From 00fc3072e484c1c6fdbd9c3b1851f866000a6cb9 Mon Sep 17 00:00:00 2001 From: Stefan Richter <stefanr@s5r6.in-berlin.de> Date: Mon, 16 Feb 2009 23:42:31 +0100 Subject: ieee1394: remove superfluous assertions hpsb_read, hpsb_write, hpsb_lock are sleeping functions which nobody is in danger to use in atomic context. Besides, in_interrupt does not cover all types of atomic context. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/ieee1394/ieee1394_transactions.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index 24021d2f0a75..675b3135d5f1 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -501,8 +501,6 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, if (length == 0) return -EINVAL; - BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet - packet = hpsb_make_readpacket(host, node, addr, length); if (!packet) { @@ -550,8 +548,6 @@ int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, if (length == 0) return -EINVAL; - BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet - packet = hpsb_make_writepacket(host, node, addr, buffer, length); if (!packet) @@ -577,8 +573,6 @@ int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, struct hpsb_packet *packet; int retval = 0; - BUG_ON(in_interrupt()); - packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); if (!packet) return -ENOMEM; -- cgit v1.2.3 From 8ae83cdf3297d7da301af36bdb6ff45bd331c6d0 Mon Sep 17 00:00:00 2001 From: Stefan Richter <stefanr@s5r6.in-berlin.de> Date: Sun, 2 Nov 2008 13:45:00 +0100 Subject: firedtv: cleanups and minor fixes Combination of the following changes: Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: increase FCP frame length for DVB-S2 tune QSPK The last three bytes didn't go out to the wire. Effect of the fix not yet tested. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: replace mdelay by msleep These functions can sleep (and in fact sleep for the duration of a whole FCP transaction). Hence msleep is more appropriate here. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: trivial reorganization in avc_api Reduce nesting level by factoring code out of avc_tuner_dsd() into helper functions. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: trivial cleanups in avc_api Use dev_err(), no CamelCase function names, adjust comment style, put #if 0 around unused code and add FIXME comments, standardize on lower-case hexadecimal constants, use ALIGN() for some frame length calculations, make a local function static... The code which writes FCP command frames and reads FCP response frames is not yet brought into canonical kernel coding style because this involves changes of typedefs (on-the-wire bitfields). Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: don't retry oPCR updates endlessly In the theoretical case that the target node wasn't handling the lock transactions as expected or there was continued interference by other initiating nodes, these functions wouldn't return for ages. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: remove bitfield typedefs from cmp, fix for big endian CPUs Use macros/ inline functions/ standard byte order accessors to read and write oPCR register values (big endian bitfields, on-the-wire data). The new code may not be the ultimate optimum, but it doesn't occur in a hot path. This fixes the CMP code for big endian CPUs. So far I tested it only on a little endian CPU though. For now, include <asm/byteorder.h> instead of <linux/byteorder.h> because drivers/ieee1394/*.h also include the former. I will fix this in drivers/ieee1394 and firedtv later. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: trivial cleanups in cmp Reduce nesting level by means of early exit and goto. Remove obsolete includes, use dev_err(), no CamelCase function names... Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: trivial cleanups in firesat-ci Whitespace, variable names, comment style... Also, use dvb_generic_open() and dvb_generic_release() directly as our hooks in struct file_operations because firedtv's wrappers merely called these generic functions. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: remove CA debug code This looks like it is not necessary to have available for endusers who cannot patch kernels for bug reporting and tests of fixes. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: remove AV/C debug code This looks like it is not necessary to have available for endusers who cannot patch kernels for bug reporting and tests of fixes. Sun, 2 Nov 2008 13:45:00 +0100 (CET) firedtv: remove various debug code Most of this was already commented out. And that which wasn't is not relevant in normal use. Mon, 29 Sep 2008 19:22:48 +0200 (CEST) firedtv: register input device as child of a FireWire device Instead of one virtual input device which exists for the whole lifetime of the driver and receives events from all connected FireDTVs, register one input device for each firedtv device. These input devices will show up as children of the respective firedtv devices in the sysfs hierarchy. However, the implementation falls short because of a bug in userspace: Udev's path_id script gets stuck with 100% CPU utilization, maybe because of an assumption about the maximum ieee1394 device hierarchy depth. To avoid this bug, we use the fw-host device instead of the proper unit_directory device as parent of the input device. There is hope that the port to the new firewire stack won't be inhibited by this userspace bug because there are no fw-host devices there. Mon, 29 Sep 2008 19:21:52 +0200 (CEST) firedtv: fix string comparison and a few sparse warnings Sparse found a bug: while ((kv_buf + kv_len - 1) == '\0') should have been while (kv_buf[kv_len - 1] == '\0') We fix it by a better implementation without a temporary copy. Also fix sparse warnings of 0 instead of NULL and signedness mismatches. Mon, 29 Sep 2008 19:21:20 +0200 (CEST) firedtv: remove unused struct members and redefine an int as a bool. Mon, 29 Sep 2008 19:20:36 +0200 (CEST) firedtv: fix initialization of dvb_frontend.ops There was a NULL pointer reference if no dvb_frontend_info was found. Also, don't directly assign struct typed values to struct typed variables. Instead write out assignments to individual strcut members. This reduces module size by about 1 kB. Mon, 29 Sep 2008 19:19:41 +0200 (CEST) firedtv: remove unused dual subunit code from initialization No FireDTVs with more than one subunit exists, hence simplify the initialization for the special case of one subunit. The driver was able to check for more than one subunit but was broken for more than two subunits. While we are at it, add several missing cleanups after failure, and include a few dynamically allocated structures diretly into struct firesat instead of allocating them separately. Mon, 29 Sep 2008 19:19:08 +0200 (CEST) firedtv: add vendor_id and version to driver match table Now that nodemgr was enhanced to match against the root directory's vendor ID if there isn't one in the unit directory, use this to prevent firedtv to be bound to wrong devices by accident. Also add the AV/C software version ID to the match flags for completeness; specifier ID and software only make sense as a pair. Mon, 29 Sep 2008 19:18:30 +0200 (CEST) firedtv: use hpsb_node_read(), _write(), _lock() because they are simpler and treat the node generation more correctly. While we are at it, clean up and simplify surrounding code. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/avc_api.c | 1074 ++++++++++++------------------ drivers/media/dvb/firesat/avc_api.h | 38 +- drivers/media/dvb/firesat/cmp.c | 252 ++++--- drivers/media/dvb/firesat/cmp.h | 6 +- drivers/media/dvb/firesat/firesat-ci.c | 244 ++----- drivers/media/dvb/firesat/firesat-ci.h | 2 +- drivers/media/dvb/firesat/firesat-rc.c | 42 +- drivers/media/dvb/firesat/firesat-rc.h | 9 +- drivers/media/dvb/firesat/firesat.h | 68 +- drivers/media/dvb/firesat/firesat_1394.c | 301 ++++----- drivers/media/dvb/firesat/firesat_dvb.c | 178 ++--- drivers/media/dvb/firesat/firesat_fe.c | 223 +++---- drivers/media/dvb/firesat/firesat_iso.c | 11 +- 13 files changed, 964 insertions(+), 1484 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c index 6337f9f21d0f..56911f3df7f6 100644 --- a/drivers/media/dvb/firesat/avc_api.c +++ b/drivers/media/dvb/firesat/avc_api.c @@ -11,14 +11,16 @@ * the License, or (at your option) any later version. */ +#include <linux/bug.h> #include <linux/crc32.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/kernel.h> #include <linux/moduleparam.h> #include <linux/mutex.h> +#include <linux/string.h> #include <linux/wait.h> #include <linux/workqueue.h> -#include <asm/atomic.h> #include <ieee1394_transactions.h> #include <nodemgr.h> @@ -27,230 +29,61 @@ #include "firesat.h" #include "firesat-rc.h" -#define RESPONSE_REGISTER 0xFFFFF0000D00ULL -#define COMMAND_REGISTER 0xFFFFF0000B00ULL -#define PCR_BASE_ADDRESS 0xFFFFF0000900ULL +#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL -static unsigned int avc_comm_debug = 0; -module_param(avc_comm_debug, int, 0644); -MODULE_PARM_DESC(avc_comm_debug, "debug logging level [0..2] of AV/C communication, default is 0 (no)"); - -/* Frees an allocated packet */ -static void avc_free_packet(struct hpsb_packet *packet) -{ - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); -} - -static const char* get_ctype_string(__u8 ctype) -{ - switch(ctype) - { - case 0: - return "CONTROL"; - case 1: - return "STATUS"; - case 2: - return "SPECIFIC_INQUIRY"; - case 3: - return "NOTIFY"; - case 4: - return "GENERAL_INQUIRY"; - } - return "UNKNOWN"; -} - -static const char* get_resp_string(__u8 ctype) -{ - switch(ctype) - { - case 8: - return "NOT_IMPLEMENTED"; - case 9: - return "ACCEPTED"; - case 10: - return "REJECTED"; - case 11: - return "IN_TRANSITION"; - case 12: - return "IMPLEMENTED_STABLE"; - case 13: - return "CHANGED"; - case 15: - return "INTERIM"; - } - return "UNKNOWN"; -} - -static const char* get_subunit_address(__u8 subunit_id, __u8 subunit_type) -{ - if (subunit_id == 7 && subunit_type == 0x1F) - return "Unit"; - if (subunit_id == 0 && subunit_type == 0x05) - return "Tuner(0)"; - return "Unsupported"; -} - -static const char* get_opcode_string(__u8 opcode) -{ - switch(opcode) - { - case 0x02: - return "PlugInfo"; - case 0x08: - return "OpenDescriptor"; - case 0x09: - return "ReadDescriptor"; - case 0x18: - return "OutputPlugSignalFormat"; - case 0x31: - return "SubunitInfo"; - case 0x30: - return "UnitInfo"; - case 0xB2: - return "Power"; - case 0xC8: - return "DirectSelectInformationType"; - case 0xCB: - return "DirectSelectData"; - case 0x00: - return "Vendor"; - - } - return "Unknown"; -} - -static void log_command_frame(const AVCCmdFrm *CmdFrm) -{ - int k; - printk(KERN_INFO "AV/C Command Frame:\n"); - printk(KERN_INFO "CommandType=%s, Address=%s(0x%02X,0x%02X), " - "opcode=%s(0x%02X), length=%d\n", - get_ctype_string(CmdFrm->ctype), - get_subunit_address(CmdFrm->suid, CmdFrm->sutyp), - CmdFrm->suid, CmdFrm->sutyp, get_opcode_string(CmdFrm->opcode), - CmdFrm->opcode, CmdFrm->length); - if (avc_comm_debug > 1) { - for(k = 0; k < CmdFrm->length - 3; k++) { - if (k % 5 != 0) - printk(", "); - else if (k != 0) - printk("\n"); - printk(KERN_INFO "operand[%d] = %02X", k, - CmdFrm->operand[k]); - } - printk(KERN_INFO "\n"); - } -} - -static void log_response_frame(const AVCRspFrm *RspFrm) +static int __avc_write(struct firesat *firesat, + const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) { - int k; - printk(KERN_INFO "AV/C Response Frame:\n"); - printk(KERN_INFO "Response=%s, Address=%s(0x%02X,0x%02X), " - "opcode=%s(0x%02X), length=%d\n", get_resp_string(RspFrm->resp), - get_subunit_address(RspFrm->suid, RspFrm->sutyp), - RspFrm->suid, RspFrm->sutyp, get_opcode_string(RspFrm->opcode), - RspFrm->opcode, RspFrm->length); - if (avc_comm_debug > 1) { - for(k = 0; k < RspFrm->length - 3; k++) { - if (k % 5 != 0) - printk(KERN_INFO ", "); - else if (k != 0) - printk(KERN_INFO "\n"); - printk(KERN_INFO "operand[%d] = %02X", k, - RspFrm->operand[k]); + int err, retry; + + if (RspFrm) + firesat->avc_reply_received = false; + + for (retry = 0; retry < 6; retry++) { + err = hpsb_node_write(firesat->ud->ne, FCP_COMMAND_REGISTER, + (quadlet_t *)CmdFrm, CmdFrm->length); + if (err) { + firesat->avc_reply_received = true; + dev_err(&firesat->ud->device, + "FCP command write failed\n"); + return err; } - printk(KERN_INFO "\n"); - } -} - -static int __AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, - AVCRspFrm *RspFrm) { - struct hpsb_packet *packet; - struct node_entry *ne; - int num_tries = 0; - int packet_ok = 0; - - ne = firesat->nodeentry; - if(!ne) { - printk(KERN_ERR "%s: lost node!\n",__func__); - return -EIO; - } - - /* need all input data */ - if(!firesat || !ne || !CmdFrm) { - printk(KERN_ERR "%s: missing input data!\n",__func__); - return -EINVAL; - } - if (avc_comm_debug > 0) { - log_command_frame(CmdFrm); - } + if (!RspFrm) + return 0; - if(RspFrm) - atomic_set(&firesat->avc_reply_received, 0); - - while (packet_ok == 0 && num_tries < 6) { - num_tries++; - packet_ok = 1; - packet = hpsb_make_writepacket(ne->host, ne->nodeid, - COMMAND_REGISTER, - (quadlet_t*)CmdFrm, - CmdFrm->length); - hpsb_set_packet_complete_task(packet, - (void (*)(void*))avc_free_packet, - packet); - hpsb_node_fill_packet(ne, packet); - - if (hpsb_send_packet(packet) < 0) { - avc_free_packet(packet); - atomic_set(&firesat->avc_reply_received, 1); - printk(KERN_ERR "%s: send failed!\n",__func__); - return -EIO; - } + /* + * AV/C specs say that answers should be sent within 150 ms. + * Time out after 200 ms. + */ + if (wait_event_timeout(firesat->avc_wait, + firesat->avc_reply_received, + HZ / 5) != 0) { + memcpy(RspFrm, firesat->respfrm, firesat->resp_length); + RspFrm->length = firesat->resp_length; - if(RspFrm) { - // AV/C specs say that answers should be send within - // 150 ms so let's time out after 200 ms - if (wait_event_timeout(firesat->avc_wait, - atomic_read(&firesat->avc_reply_received) == 1, - HZ / 5) == 0) { - packet_ok = 0; - } - else { - memcpy(RspFrm, firesat->respfrm, - firesat->resp_length); - RspFrm->length = firesat->resp_length; - if (avc_comm_debug > 0) { - log_response_frame(RspFrm); - } - } + return 0; } } - if (packet_ok == 0) { - printk(KERN_ERR "%s: AV/C response timed out 6 times.\n", - __func__); - return -ETIMEDOUT; - } - - return 0; + dev_err(&firesat->ud->device, "FCP response timed out\n"); + return -ETIMEDOUT; } -int AVCWrite(struct firesat*firesat, const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) +static int avc_write(struct firesat *firesat, + const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) { int ret; if (mutex_lock_interruptible(&firesat->avc_mutex)) return -EINTR; - ret = __AVCWrite(firesat, CmdFrm, RspFrm); + ret = __avc_write(firesat, CmdFrm, RspFrm); mutex_unlock(&firesat->avc_mutex); return ret; } -int AVCRecv(struct firesat *firesat, u8 *data, size_t length) +int avc_recv(struct firesat *firesat, u8 *data, size_t length) { AVCRspFrm *RspFrm = (AVCRspFrm *)data; @@ -260,87 +93,64 @@ int AVCRecv(struct firesat *firesat, u8 *data, size_t length) RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { if (RspFrm->resp == CHANGED) { - firesat_handle_rc(RspFrm->operand[4] << 8 | - RspFrm->operand[5]); + firesat_handle_rc(firesat, + RspFrm->operand[4] << 8 | RspFrm->operand[5]); schedule_work(&firesat->remote_ctrl_work); } else if (RspFrm->resp != INTERIM) { - printk(KERN_INFO "firedtv: remote control result = " - "%d\n", RspFrm->resp); + dev_info(&firesat->ud->device, + "remote control result = %d\n", RspFrm->resp); } return 0; } - if(atomic_read(&firesat->avc_reply_received) == 1) { - printk(KERN_ERR "%s: received out-of-order AVC response, " - "ignored\n",__func__); - return -EINVAL; + if (firesat->avc_reply_received) { + dev_err(&firesat->ud->device, + "received out-of-order AVC response, ignored\n"); + return -EIO; } -// AVCRspFrm *resp=(AVCRspFrm *)data; -// int k; - -// printk(KERN_INFO "resp=0x%x\n",resp->resp); -// printk(KERN_INFO "cts=0x%x\n",resp->cts); -// printk(KERN_INFO "suid=0x%x\n",resp->suid); -// printk(KERN_INFO "sutyp=0x%x\n",resp->sutyp); -// printk(KERN_INFO "opcode=0x%x\n",resp->opcode); -// printk(KERN_INFO "length=%d\n",resp->length); -// for(k=0;k<2;k++) -// printk(KERN_INFO "operand[%d]=%02x\n",k,resp->operand[k]); + memcpy(firesat->respfrm, data, length); + firesat->resp_length = length; - memcpy(firesat->respfrm,data,length); - firesat->resp_length=length; - - atomic_set(&firesat->avc_reply_received, 1); + firesat->avc_reply_received = true; wake_up(&firesat->avc_wait); return 0; } -// tuning command for setting the relative LNB frequency (not supported by the AVC standard) -static void AVCTuner_tuneQPSK(struct firesat *firesat, struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) { - - memset(CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm->cts = AVC; - CmdFrm->ctype = CONTROL; - CmdFrm->sutyp = 0x5; - CmdFrm->suid = firesat->subunit; +/* + * tuning command for setting the relative LNB frequency + * (not supported by the AVC standard) + */ +static void avc_tuner_tuneqpsk(struct firesat *firesat, + struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) +{ CmdFrm->opcode = VENDOR; - CmdFrm->operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm->operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm->operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm->operand[3]=SFE_VENDOR_OPCODE_TUNE_QPSK; - - printk(KERN_INFO "%s: tuning to frequency %u\n",__func__,params->frequency); - - CmdFrm->operand[4] = (params->frequency >> 24) & 0xFF; - CmdFrm->operand[5] = (params->frequency >> 16) & 0xFF; - CmdFrm->operand[6] = (params->frequency >> 8) & 0xFF; - CmdFrm->operand[7] = params->frequency & 0xFF; + CmdFrm->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + CmdFrm->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + CmdFrm->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + CmdFrm->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; - printk(KERN_INFO "%s: symbol rate = %uBd\n",__func__,params->u.qpsk.symbol_rate); + CmdFrm->operand[4] = (params->frequency >> 24) & 0xff; + CmdFrm->operand[5] = (params->frequency >> 16) & 0xff; + CmdFrm->operand[6] = (params->frequency >> 8) & 0xff; + CmdFrm->operand[7] = params->frequency & 0xff; - CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate/1000) >> 8) & 0xFF; - CmdFrm->operand[9] = (params->u.qpsk.symbol_rate/1000) & 0xFF; + CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; + CmdFrm->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; switch(params->u.qpsk.fec_inner) { case FEC_1_2: - CmdFrm->operand[10] = 0x1; - break; + CmdFrm->operand[10] = 0x1; break; case FEC_2_3: - CmdFrm->operand[10] = 0x2; - break; + CmdFrm->operand[10] = 0x2; break; case FEC_3_4: - CmdFrm->operand[10] = 0x3; - break; + CmdFrm->operand[10] = 0x3; break; case FEC_5_6: - CmdFrm->operand[10] = 0x4; - break; + CmdFrm->operand[10] = 0x4; break; case FEC_7_8: - CmdFrm->operand[10] = 0x5; - break; + CmdFrm->operand[10] = 0x5; break; case FEC_4_5: case FEC_8_9: case FEC_AUTO: @@ -348,278 +158,287 @@ static void AVCTuner_tuneQPSK(struct firesat *firesat, struct dvb_frontend_param CmdFrm->operand[10] = 0x0; } - if(firesat->voltage == 0xff) + if (firesat->voltage == 0xff) CmdFrm->operand[11] = 0xff; + else if (firesat->voltage == SEC_VOLTAGE_18) /* polarisation */ + CmdFrm->operand[11] = 0; else - CmdFrm->operand[11] = (firesat->voltage==SEC_VOLTAGE_18)?0:1; // polarisation - if(firesat->tone == 0xff) + CmdFrm->operand[11] = 1; + + if (firesat->tone == 0xff) CmdFrm->operand[12] = 0xff; + else if (firesat->tone == SEC_TONE_ON) /* band */ + CmdFrm->operand[12] = 1; else - CmdFrm->operand[12] = (firesat->tone==SEC_TONE_ON)?1:0; // band + CmdFrm->operand[12] = 0; if (firesat->type == FireSAT_DVB_S2) { CmdFrm->operand[13] = 0x1; - CmdFrm->operand[14] = 0xFF; - CmdFrm->operand[15] = 0xFF; + CmdFrm->operand[14] = 0xff; + CmdFrm->operand[15] = 0xff; + CmdFrm->length = 20; + } else { + CmdFrm->length = 16; } +} + +static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, + AVCCmdFrm *CmdFrm) +{ + M_VALID_FLAGS flags; - CmdFrm->length = 16; + flags.Bits.Modulation = params->u.qam.modulation != QAM_AUTO; + flags.Bits.FEC_inner = params->u.qam.fec_inner != FEC_AUTO; + flags.Bits.FEC_outer = 0; + flags.Bits.Symbol_Rate = 1; + flags.Bits.Frequency = 1; + flags.Bits.Orbital_Pos = 0; + flags.Bits.Polarisation = 0; + flags.Bits.reserved_fields = 0; + flags.Bits.reserved1 = 0; + flags.Bits.Network_ID = 0; + + CmdFrm->opcode = DSD; + + CmdFrm->operand[0] = 0; /* source plug */ + CmdFrm->operand[1] = 0xd2; /* subfunction replace */ + CmdFrm->operand[2] = 0x20; /* system id = DVB */ + CmdFrm->operand[3] = 0x00; /* antenna number */ + /* system_specific_multiplex selection_length */ + CmdFrm->operand[4] = 0x11; + CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ + CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ + CmdFrm->operand[7] = 0x00; + CmdFrm->operand[8] = 0x00; + CmdFrm->operand[9] = 0x00; + CmdFrm->operand[10] = 0x00; + + CmdFrm->operand[11] = + (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); + CmdFrm->operand[12] = + ((params->frequency / 4000) >> 8) & 0xff; + CmdFrm->operand[13] = (params->frequency / 4000) & 0xff; + CmdFrm->operand[14] = + ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; + CmdFrm->operand[15] = + ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; + CmdFrm->operand[16] = + ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; + CmdFrm->operand[17] = 0x00; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: + CmdFrm->operand[18] = 0x1; break; + case FEC_2_3: + CmdFrm->operand[18] = 0x2; break; + case FEC_3_4: + CmdFrm->operand[18] = 0x3; break; + case FEC_5_6: + CmdFrm->operand[18] = 0x4; break; + case FEC_7_8: + CmdFrm->operand[18] = 0x5; break; + case FEC_8_9: + CmdFrm->operand[18] = 0x6; break; + case FEC_4_5: + CmdFrm->operand[18] = 0x8; break; + case FEC_AUTO: + default: + CmdFrm->operand[18] = 0x0; + } + switch (params->u.qam.modulation) { + case QAM_16: + CmdFrm->operand[19] = 0x08; break; + case QAM_32: + CmdFrm->operand[19] = 0x10; break; + case QAM_64: + CmdFrm->operand[19] = 0x18; break; + case QAM_128: + CmdFrm->operand[19] = 0x20; break; + case QAM_256: + CmdFrm->operand[19] = 0x28; break; + case QAM_AUTO: + default: + CmdFrm->operand[19] = 0x00; + } + CmdFrm->operand[20] = 0x00; + CmdFrm->operand[21] = 0x00; + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + CmdFrm->operand[22] = 0x00; + + CmdFrm->length = 28; } -int AVCTuner_DSD(struct firesat *firesat, struct dvb_frontend_parameters *params, __u8 *status) { +static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, + AVCCmdFrm *CmdFrm) +{ + M_VALID_FLAGS flags; + + flags.Bits_T.GuardInterval = + params->u.ofdm.guard_interval != GUARD_INTERVAL_AUTO; + flags.Bits_T.CodeRateLPStream = + params->u.ofdm.code_rate_LP != FEC_AUTO; + flags.Bits_T.CodeRateHPStream = + params->u.ofdm.code_rate_HP != FEC_AUTO; + flags.Bits_T.HierarchyInfo = + params->u.ofdm.hierarchy_information != HIERARCHY_AUTO; + flags.Bits_T.Constellation = + params->u.ofdm.constellation != QAM_AUTO; + flags.Bits_T.Bandwidth = + params->u.ofdm.bandwidth != BANDWIDTH_AUTO; + flags.Bits_T.CenterFrequency = 1; + flags.Bits_T.reserved1 = 0; + flags.Bits_T.reserved2 = 0; + flags.Bits_T.OtherFrequencyFlag = 0; + flags.Bits_T.TransmissionMode = + params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO; + flags.Bits_T.NetworkId = 0; + + CmdFrm->opcode = DSD; + + CmdFrm->operand[0] = 0; /* source plug */ + CmdFrm->operand[1] = 0xd2; /* subfunction replace */ + CmdFrm->operand[2] = 0x20; /* system id = DVB */ + CmdFrm->operand[3] = 0x00; /* antenna number */ + /* system_specific_multiplex selection_length */ + CmdFrm->operand[4] = 0x0c; + CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ + CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ + CmdFrm->operand[7] = 0x0; + CmdFrm->operand[8] = (params->frequency / 10) >> 24; + CmdFrm->operand[9] = ((params->frequency / 10) >> 16) & 0xff; + CmdFrm->operand[10] = ((params->frequency / 10) >> 8) & 0xff; + CmdFrm->operand[11] = (params->frequency / 10) & 0xff; + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_7_MHZ: + CmdFrm->operand[12] = 0x20; break; + case BANDWIDTH_8_MHZ: + case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ + case BANDWIDTH_AUTO: + default: + CmdFrm->operand[12] = 0x00; + } + switch (params->u.ofdm.constellation) { + case QAM_16: + CmdFrm->operand[13] = 1 << 6; break; + case QAM_64: + CmdFrm->operand[13] = 2 << 6; break; + case QPSK: + default: + CmdFrm->operand[13] = 0x00; + } + switch (params->u.ofdm.hierarchy_information) { + case HIERARCHY_1: + CmdFrm->operand[13] |= 1 << 3; break; + case HIERARCHY_2: + CmdFrm->operand[13] |= 2 << 3; break; + case HIERARCHY_4: + CmdFrm->operand[13] |= 3 << 3; break; + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + default: + break; + } + switch (params->u.ofdm.code_rate_HP) { + case FEC_2_3: + CmdFrm->operand[13] |= 1; break; + case FEC_3_4: + CmdFrm->operand[13] |= 2; break; + case FEC_5_6: + CmdFrm->operand[13] |= 3; break; + case FEC_7_8: + CmdFrm->operand[13] |= 4; break; + case FEC_1_2: + default: + break; + } + switch (params->u.ofdm.code_rate_LP) { + case FEC_2_3: + CmdFrm->operand[14] = 1 << 5; break; + case FEC_3_4: + CmdFrm->operand[14] = 2 << 5; break; + case FEC_5_6: + CmdFrm->operand[14] = 3 << 5; break; + case FEC_7_8: + CmdFrm->operand[14] = 4 << 5; break; + case FEC_1_2: + default: + CmdFrm->operand[14] = 0x00; break; + } + switch (params->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_16: + CmdFrm->operand[14] |= 1 << 3; break; + case GUARD_INTERVAL_1_8: + CmdFrm->operand[14] |= 2 << 3; break; + case GUARD_INTERVAL_1_4: + CmdFrm->operand[14] |= 3 << 3; break; + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + default: + break; + } + switch (params->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_8K: + CmdFrm->operand[14] |= 1 << 1; break; + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + default: + break; + } + + CmdFrm->operand[15] = 0x00; /* network_ID[0] */ + CmdFrm->operand[16] = 0x00; /* network_ID[1] */ + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + CmdFrm->operand[17] = 0x00; + + CmdFrm->length = 24; +} + +int avc_tuner_dsd(struct firesat *firesat, + struct dvb_frontend_parameters *params) +{ AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; - M_VALID_FLAGS flags; - int k; - -// printk(KERN_INFO "%s\n", __func__); - - if (firesat->type == FireSAT_DVB_S || firesat->type == FireSAT_DVB_S2) - AVCTuner_tuneQPSK(firesat, params, &CmdFrm); - else { - if(firesat->type == FireSAT_DVB_T) { - flags.Bits_T.GuardInterval = (params->u.ofdm.guard_interval != GUARD_INTERVAL_AUTO); - flags.Bits_T.CodeRateLPStream = (params->u.ofdm.code_rate_LP != FEC_AUTO); - flags.Bits_T.CodeRateHPStream = (params->u.ofdm.code_rate_HP != FEC_AUTO); - flags.Bits_T.HierarchyInfo = (params->u.ofdm.hierarchy_information != HIERARCHY_AUTO); - flags.Bits_T.Constellation = (params->u.ofdm.constellation != QAM_AUTO); - flags.Bits_T.Bandwidth = (params->u.ofdm.bandwidth != BANDWIDTH_AUTO); - flags.Bits_T.CenterFrequency = 1; - flags.Bits_T.reserved1 = 0; - flags.Bits_T.reserved2 = 0; - flags.Bits_T.OtherFrequencyFlag = 0; - flags.Bits_T.TransmissionMode = (params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO); - flags.Bits_T.NetworkId = 0; - } else { - flags.Bits.Modulation = - (params->u.qam.modulation != QAM_AUTO); - flags.Bits.FEC_inner = - (params->u.qam.fec_inner != FEC_AUTO); - flags.Bits.FEC_outer = 0; - flags.Bits.Symbol_Rate = 1; - flags.Bits.Frequency = 1; - flags.Bits.Orbital_Pos = 0; - flags.Bits.Polarisation = 0; - flags.Bits.reserved_fields = 0; - flags.Bits.reserved1 = 0; - flags.Bits.Network_ID = 0; - } - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = DSD; - - CmdFrm.operand[0] = 0; // source plug - CmdFrm.operand[1] = 0xD2; // subfunction replace - CmdFrm.operand[2] = 0x20; // system id = DVB - CmdFrm.operand[3] = 0x00; // antenna number - // system_specific_multiplex selection_length - CmdFrm.operand[4] = (firesat->type == FireSAT_DVB_T)?0x0c:0x11; - CmdFrm.operand[5] = flags.Valid_Word.ByteHi; // valid_flags [0] - CmdFrm.operand[6] = flags.Valid_Word.ByteLo; // valid_flags [1] - - if(firesat->type == FireSAT_DVB_T) { - CmdFrm.operand[7] = 0x0; - CmdFrm.operand[8] = (params->frequency/10) >> 24; - CmdFrm.operand[9] = - ((params->frequency/10) >> 16) & 0xFF; - CmdFrm.operand[10] = - ((params->frequency/10) >> 8) & 0xFF; - CmdFrm.operand[11] = (params->frequency/10) & 0xFF; - switch(params->u.ofdm.bandwidth) { - case BANDWIDTH_7_MHZ: - CmdFrm.operand[12] = 0x20; - break; - case BANDWIDTH_8_MHZ: - case BANDWIDTH_6_MHZ: // not defined by AVC spec - case BANDWIDTH_AUTO: - default: - CmdFrm.operand[12] = 0x00; - } - switch(params->u.ofdm.constellation) { - case QAM_16: - CmdFrm.operand[13] = 1 << 6; - break; - case QAM_64: - CmdFrm.operand[13] = 2 << 6; - break; - case QPSK: - default: - CmdFrm.operand[13] = 0x00; - } - switch(params->u.ofdm.hierarchy_information) { - case HIERARCHY_1: - CmdFrm.operand[13] |= 1 << 3; - break; - case HIERARCHY_2: - CmdFrm.operand[13] |= 2 << 3; - break; - case HIERARCHY_4: - CmdFrm.operand[13] |= 3 << 3; - break; - case HIERARCHY_AUTO: - case HIERARCHY_NONE: - default: - break; - } - switch(params->u.ofdm.code_rate_HP) { - case FEC_2_3: - CmdFrm.operand[13] |= 1; - break; - case FEC_3_4: - CmdFrm.operand[13] |= 2; - break; - case FEC_5_6: - CmdFrm.operand[13] |= 3; - break; - case FEC_7_8: - CmdFrm.operand[13] |= 4; - break; - case FEC_1_2: - default: - break; - } - switch(params->u.ofdm.code_rate_LP) { - case FEC_2_3: - CmdFrm.operand[14] = 1 << 5; - break; - case FEC_3_4: - CmdFrm.operand[14] = 2 << 5; - break; - case FEC_5_6: - CmdFrm.operand[14] = 3 << 5; - break; - case FEC_7_8: - CmdFrm.operand[14] = 4 << 5; - break; - case FEC_1_2: - default: - CmdFrm.operand[14] = 0x00; - break; - } - switch(params->u.ofdm.guard_interval) { - case GUARD_INTERVAL_1_16: - CmdFrm.operand[14] |= 1 << 3; - break; - case GUARD_INTERVAL_1_8: - CmdFrm.operand[14] |= 2 << 3; - break; - case GUARD_INTERVAL_1_4: - CmdFrm.operand[14] |= 3 << 3; - break; - case GUARD_INTERVAL_1_32: - case GUARD_INTERVAL_AUTO: - default: - break; - } - switch(params->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_8K: - CmdFrm.operand[14] |= 1 << 1; - break; - case TRANSMISSION_MODE_2K: - case TRANSMISSION_MODE_AUTO: - default: - break; - } - - CmdFrm.operand[15] = 0x00; // network_ID[0] - CmdFrm.operand[16] = 0x00; // network_ID[1] - CmdFrm.operand[17] = 0x00; // Nr_of_dsd_sel_specs = 0 - > No PIDs are transmitted - - CmdFrm.length = 24; - } else { - CmdFrm.operand[7] = 0x00; - CmdFrm.operand[8] = 0x00; - CmdFrm.operand[9] = 0x00; - CmdFrm.operand[10] = 0x00; - - CmdFrm.operand[11] = - (((params->frequency/4000) >> 16) & 0xFF) | (2 << 6); - CmdFrm.operand[12] = - ((params->frequency/4000) >> 8) & 0xFF; - CmdFrm.operand[13] = (params->frequency/4000) & 0xFF; - CmdFrm.operand[14] = - ((params->u.qpsk.symbol_rate/1000) >> 12) & 0xFF; - CmdFrm.operand[15] = - ((params->u.qpsk.symbol_rate/1000) >> 4) & 0xFF; - CmdFrm.operand[16] = - ((params->u.qpsk.symbol_rate/1000) << 4) & 0xF0; - CmdFrm.operand[17] = 0x00; - switch(params->u.qpsk.fec_inner) { - case FEC_1_2: - CmdFrm.operand[18] = 0x1; - break; - case FEC_2_3: - CmdFrm.operand[18] = 0x2; - break; - case FEC_3_4: - CmdFrm.operand[18] = 0x3; - break; - case FEC_5_6: - CmdFrm.operand[18] = 0x4; - break; - case FEC_7_8: - CmdFrm.operand[18] = 0x5; - break; - case FEC_8_9: - CmdFrm.operand[18] = 0x6; - break; - case FEC_4_5: - CmdFrm.operand[18] = 0x8; - break; - case FEC_AUTO: - default: - CmdFrm.operand[18] = 0x0; - } - switch(params->u.qam.modulation) { - case QAM_16: - CmdFrm.operand[19] = 0x08; // modulation - break; - case QAM_32: - CmdFrm.operand[19] = 0x10; // modulation - break; - case QAM_64: - CmdFrm.operand[19] = 0x18; // modulation - break; - case QAM_128: - CmdFrm.operand[19] = 0x20; // modulation - break; - case QAM_256: - CmdFrm.operand[19] = 0x28; // modulation - break; - case QAM_AUTO: - default: - CmdFrm.operand[19] = 0x00; // modulation - } - CmdFrm.operand[20] = 0x00; - CmdFrm.operand[21] = 0x00; - CmdFrm.operand[22] = 0x00; // Nr_of_dsd_sel_specs = 0 - > No PIDs are transmitted - - CmdFrm.length=28; - } - } // AVCTuner_DSD_direct + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - if((k=AVCWrite(firesat,&CmdFrm,&RspFrm))) - return k; + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = firesat->subunit; - mdelay(500); + switch (firesat->type) { + case FireSAT_DVB_S: + case FireSAT_DVB_S2: + avc_tuner_tuneqpsk(firesat, params, &CmdFrm); break; + case FireSAT_DVB_C: + avc_tuner_dsd_dvb_c(params, &CmdFrm); break; + case FireSAT_DVB_T: + avc_tuner_dsd_dvb_t(params, &CmdFrm); break; + default: + BUG(); + } + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) + return -EIO; + + msleep(500); +#if 0 + /* FIXME: */ + /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ if(status) *status=RspFrm.operand[2]; +#endif return 0; } -int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) +int avc_tuner_set_pids(struct firesat *firesat, unsigned char pidc, u16 pid[]) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; - int pos,k; + int pos, k; - if(pidc > 16 && pidc != 0xFF) + if (pidc > 16 && pidc != 0xff) return -EINVAL; memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); @@ -637,9 +456,9 @@ int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) CmdFrm.operand[4] = 0x00; // system_specific_multiplex selection_length CmdFrm.operand[5] = pidc; // Nr_of_dsd_sel_specs - pos=6; - if(pidc != 0xFF) { - for(k=0;k<pidc;k++) { + pos = 6; + if (pidc != 0xff) + for (k = 0; k < pidc; k++) { CmdFrm.operand[pos++] = 0x13; // flowfunction relay CmdFrm.operand[pos++] = 0x80; // dsd_sel_spec_valid_flags -> PID CmdFrm.operand[pos++] = (pid[k] >> 8) & 0x1F; @@ -647,25 +466,20 @@ int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]) CmdFrm.operand[pos++] = 0x00; // tableID CmdFrm.operand[pos++] = 0x00; // filter_length } - } - CmdFrm.length = pos+3; - if((pos+3)%4) - CmdFrm.length += 4 - ((pos+3)%4); + CmdFrm.length = ALIGN(3 + pos, 4); - if((k=AVCWrite(firesat,&CmdFrm,&RspFrm))) - return k; + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) + return -EIO; - mdelay(50); + msleep(50); return 0; } -int AVCTuner_GetTS(struct firesat *firesat){ +int avc_tuner_get_ts(struct firesat *firesat) +{ AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; - int k; - - //printk(KERN_INFO "%s\n", __func__); memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); @@ -688,14 +502,14 @@ int AVCTuner_GetTS(struct firesat *firesat){ CmdFrm.length = (firesat->type == FireSAT_DVB_T)?24:28; - if ((k=AVCWrite(firesat, &CmdFrm, &RspFrm))) - return k; + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) + return -EIO; - mdelay(250); + msleep(250); return 0; } -int AVCIdentifySubunit(struct firesat *firesat) +int avc_identify_subunit(struct firesat *firesat) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; @@ -718,22 +532,21 @@ int AVCIdentifySubunit(struct firesat *firesat) CmdFrm.length=12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm)<0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; - if(RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { - printk(KERN_ERR "%s: AVCWrite returned error code %d\n", - __func__, RspFrm.resp); - return -EINVAL; - } - if(((RspFrm.operand[3] << 8) + RspFrm.operand[4]) != 8) { - printk(KERN_ERR "%s: Invalid response length\n", __func__); + if ((RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) || + (RspFrm.operand[3] << 8) + RspFrm.operand[4] != 8) { + dev_err(&firesat->ud->device, + "cannot read subunit identifier\n"); return -EINVAL; } return 0; } -int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_info) { +int avc_tuner_status(struct firesat *firesat, + ANTENNA_INPUT_INFO *antenna_input_info) +{ AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; int length; @@ -754,37 +567,32 @@ int AVCTunerStatus(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_in CmdFrm.operand[5]=0x00; CmdFrm.operand[6]=0x00; CmdFrm.length=12; - if (AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; - if(RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { - printk(KERN_ERR "%s: AVCWrite returned code %d\n", - __func__, RspFrm.resp); + if (RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { + dev_err(&firesat->ud->device, "cannot read tuner status\n"); return -EINVAL; } length = RspFrm.operand[9]; - if(RspFrm.operand[1] == 0x10 && length == sizeof(ANTENNA_INPUT_INFO)) - { - memcpy(antenna_input_info, &RspFrm.operand[10], - sizeof(ANTENNA_INPUT_INFO)); - return 0; + if (RspFrm.operand[1] != 0x10 || length != sizeof(ANTENNA_INPUT_INFO)) { + dev_err(&firesat->ud->device, "got invalid tuner status\n"); + return -EINVAL; } - printk(KERN_ERR "%s: invalid tuner status (op=%d,length=%d) returned " - "from AVC\n", __func__, RspFrm.operand[1], length); - return -EINVAL; + + memcpy(antenna_input_info, &RspFrm.operand[10], length); + return 0; } -int AVCLNBControl(struct firesat *firesat, char voltage, char burst, - char conttone, char nrdiseq, - struct dvb_diseqc_master_cmd *diseqcmd) +int avc_lnb_control(struct firesat *firesat, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; - int i,j; - - printk(KERN_INFO "%s: voltage = %x, burst = %x, conttone = %x\n", - __func__, voltage, burst, conttone); + int i, j, k; memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); @@ -804,85 +612,33 @@ int AVCLNBControl(struct firesat *firesat, char voltage, char burst, i=6; - for(j=0;j<nrdiseq;j++) { - int k; - printk(KERN_INFO "%s: diseq %d len %x\n", - __func__, j, diseqcmd[j].msg_len); - CmdFrm.operand[i++]=diseqcmd[j].msg_len; + for (j = 0; j < nrdiseq; j++) { + CmdFrm.operand[i++] = diseqcmd[j].msg_len; - for(k=0;k<diseqcmd[j].msg_len;k++) { - printk(KERN_INFO "%s: diseq %d msg[%d] = %x\n", - __func__, j, k, diseqcmd[j].msg[k]); - CmdFrm.operand[i++]=diseqcmd[j].msg[k]; - } + for (k = 0; k < diseqcmd[j].msg_len; k++) + CmdFrm.operand[i++] = diseqcmd[j].msg[k]; } CmdFrm.operand[i++]=burst; CmdFrm.operand[i++]=conttone; - CmdFrm.length=i+3; - if((i+3)%4) - CmdFrm.length += 4 - ((i+3)%4); + CmdFrm.length = ALIGN(3 + i, 4); -/* for(j=0;j<CmdFrm.length;j++) - printk(KERN_INFO "%s: CmdFrm.operand[%d]=0x%x\n",__func__,j,CmdFrm.operand[j]); - - printk(KERN_INFO "%s: cmdfrm.length = %u\n",__func__,CmdFrm.length); - */ - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; - if(RspFrm.resp != ACCEPTED) { - printk(KERN_ERR "%s: AVCWrite returned code %d\n", - __func__, RspFrm.resp); - return -EINVAL; - } - - return 0; -} - -int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x1f; - CmdFrm.suid = 0x7; - CmdFrm.opcode = SUBUNIT_Info; - - CmdFrm.operand[0] = 0x07; - CmdFrm.operand[1] = 0xff; - CmdFrm.operand[2] = 0xff; - CmdFrm.operand[3] = 0xff; - CmdFrm.operand[4] = 0xff; - - CmdFrm.length = 8; - - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) - return -EIO; - - if(RspFrm.resp != STABLE) { - printk(KERN_ERR "%s: AVCWrite returned code %d\n", - __func__, RspFrm.resp); + if (RspFrm.resp != ACCEPTED) { + dev_err(&firesat->ud->device, "LNB control failed\n"); return -EINVAL; } - if(subunitcount) - *subunitcount = (RspFrm.operand[1] & 0x7) + 1; - return 0; } -int AVCRegisterRemoteControl(struct firesat *firesat) +int avc_register_remote_control(struct firesat *firesat) { AVCCmdFrm CmdFrm; -// printk(KERN_INFO "%s\n",__func__); - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); CmdFrm.cts = AVC; @@ -898,7 +654,7 @@ int AVCRegisterRemoteControl(struct firesat *firesat) CmdFrm.length = 8; - return AVCWrite(firesat, &CmdFrm, NULL); + return avc_write(firesat, &CmdFrm, NULL); } void avc_remote_ctrl_work(struct work_struct *work) @@ -907,12 +663,12 @@ void avc_remote_ctrl_work(struct work_struct *work) container_of(work, struct firesat, remote_ctrl_work); /* Should it be rescheduled in failure cases? */ - AVCRegisterRemoteControl(firesat); + avc_register_remote_control(firesat); } -int AVCTuner_Host2Ca(struct firesat *firesat) +#if 0 /* FIXME: unused */ +int avc_tuner_host2ca(struct firesat *firesat) { - AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; @@ -933,37 +689,39 @@ int AVCTuner_Host2Ca(struct firesat *firesat) CmdFrm.operand[7] = 0; // length CmdFrm.length = 12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; return 0; } +#endif static int get_ca_object_pos(AVCRspFrm *RspFrm) { int length = 1; - // Check length of length field + /* Check length of length field */ if (RspFrm->operand[7] & 0x80) - length = (RspFrm->operand[7] & 0x7F) + 1; + length = (RspFrm->operand[7] & 0x7f) + 1; return length + 7; } static int get_ca_object_length(AVCRspFrm *RspFrm) { +#if 0 /* FIXME: unused */ int size = 0; int i; - if (RspFrm->operand[7] & 0x80) { - for (i = 0; i < (RspFrm->operand[7] & 0x7F); i++) { + if (RspFrm->operand[7] & 0x80) + for (i = 0; i < (RspFrm->operand[7] & 0x7f); i++) { size <<= 8; size += RspFrm->operand[8 + i]; } - } +#endif return RspFrm->operand[7]; } -int avc_ca_app_info(struct firesat *firesat, char *app_info, int *length) +int avc_ca_app_info(struct firesat *firesat, char *app_info, unsigned int *len) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; @@ -984,9 +742,10 @@ int avc_ca_app_info(struct firesat *firesat, char *app_info, int *length) CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag CmdFrm.length = 12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; + /* FIXME: check response code and validate response data */ pos = get_ca_object_pos(&RspFrm); app_info[0] = (TAG_APP_INFO >> 16) & 0xFF; @@ -995,16 +754,16 @@ int avc_ca_app_info(struct firesat *firesat, char *app_info, int *length) app_info[3] = 6 + RspFrm.operand[pos + 4]; app_info[4] = 0x01; memcpy(&app_info[5], &RspFrm.operand[pos], 5 + RspFrm.operand[pos + 4]); - *length = app_info[3] + 4; + *len = app_info[3] + 4; return 0; } -int avc_ca_info(struct firesat *firesat, char *app_info, int *length) +int avc_ca_info(struct firesat *firesat, char *app_info, unsigned int *len) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; - int pos; + /* int pos; FIXME: unused */ memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); CmdFrm.cts = AVC; @@ -1021,17 +780,17 @@ int avc_ca_info(struct firesat *firesat, char *app_info, int *length) CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag CmdFrm.length = 12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; - pos = get_ca_object_pos(&RspFrm); + /* pos = get_ca_object_pos(&RspFrm); FIXME: unused */ app_info[0] = (TAG_CA_INFO >> 16) & 0xFF; app_info[1] = (TAG_CA_INFO >> 8) & 0xFF; app_info[2] = (TAG_CA_INFO >> 0) & 0xFF; app_info[3] = 2; app_info[4] = app_info[5]; app_info[5] = app_info[6]; - *length = app_info[3] + 4; + *len = app_info[3] + 4; return 0; } @@ -1059,7 +818,7 @@ int avc_ca_reset(struct firesat *firesat) CmdFrm.operand[8] = 0; // force hardware reset CmdFrm.length = 12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; return 0; @@ -1085,9 +844,8 @@ int avc_ca_pmt(struct firesat *firesat, char *msg, int length) CmdFrm.opcode = VENDOR; if (msg[0] != LIST_MANAGEMENT_ONLY) { - printk(KERN_INFO "%s: list_management %d not support. " - "Forcing list_management to \"only\" (3). \n", - __func__, msg[0]); + dev_info(&firesat->ud->device, + "forcing list_management to ONLY\n"); msg[0] = LIST_MANAGEMENT_ONLY; } // We take the cmd_id from the programme level only! @@ -1134,20 +892,17 @@ int avc_ca_pmt(struct firesat *firesat, char *msg, int length) read_pos = 6; write_pos = 22; if (program_info_length > 0) { -/* printk(KERN_INFO "Copying descriptors at programme level.\n"); */ pmt_cmd_id = msg[read_pos++]; - if (pmt_cmd_id != 1 && pmt_cmd_id !=4) { - printk(KERN_ERR "Invalid pmt_cmd_id=%d.\n", - pmt_cmd_id); - } + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(&firesat->ud->device, + "invalid pmt_cmd_id %d\n", pmt_cmd_id); + memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], program_info_length); read_pos += program_info_length; write_pos += program_info_length; } while (read_pos < length) { -/* printk(KERN_INFO "Copying descriptors at stream level for " */ -/* "stream type %d.\n", msg[read_pos]); */ CmdFrm.operand[write_pos++] = msg[read_pos++]; CmdFrm.operand[write_pos++] = msg[read_pos++]; CmdFrm.operand[write_pos++] = msg[read_pos++]; @@ -1160,10 +915,11 @@ int avc_ca_pmt(struct firesat *firesat, char *msg, int length) CmdFrm.operand[write_pos++] = es_info_length & 0xFF; if (es_info_length > 0) { pmt_cmd_id = msg[read_pos++]; - if (pmt_cmd_id != 1 && pmt_cmd_id !=4) { - printk(KERN_ERR "Invalid pmt_cmd_id=%d at " - "stream level.\n", pmt_cmd_id); - } + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(&firesat->ud->device, + "invalid pmt_cmd_id %d " + "at stream level\n", pmt_cmd_id); + memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], es_info_length); read_pos += es_info_length; @@ -1187,20 +943,18 @@ int avc_ca_pmt(struct firesat *firesat, char *msg, int length) CmdFrm.operand[write_pos - 2] = (crc32_csum >> 8) & 0xFF; CmdFrm.operand[write_pos - 1] = (crc32_csum >> 0) & 0xFF; - CmdFrm.length = write_pos + 3; - if ((write_pos + 3) % 4) - CmdFrm.length += 4 - ((write_pos + 3) % 4); + CmdFrm.length = ALIGN(3 + write_pos, 4); - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; if (RspFrm.resp != ACCEPTED) { - printk(KERN_ERR "Answer to CA PMT was %d\n", RspFrm.resp); + dev_err(&firesat->ud->device, + "CA PMT failed with response 0x%x\n", RspFrm.resp); return -EFAULT; } return 0; - } int avc_ca_get_time_date(struct firesat *firesat, int *interval) @@ -1225,9 +979,11 @@ int avc_ca_get_time_date(struct firesat *firesat, int *interval) CmdFrm.operand[7] = 0; // length CmdFrm.length = 12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; + /* FIXME: check response code and validate response data */ + *interval = RspFrm.operand[get_ca_object_pos(&RspFrm)]; return 0; @@ -1255,13 +1011,13 @@ int avc_ca_enter_menu(struct firesat *firesat) CmdFrm.operand[7] = 0; // length CmdFrm.length = 12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; return 0; } -int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, int *length) +int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, unsigned int *len) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; @@ -1283,11 +1039,13 @@ int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, int *length) CmdFrm.operand[7] = 0; // length CmdFrm.length = 12; - if(AVCWrite(firesat,&CmdFrm,&RspFrm) < 0) + if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; - *length = get_ca_object_length(&RspFrm); - memcpy(mmi_object, &RspFrm.operand[get_ca_object_pos(&RspFrm)], *length); + /* FIXME: check response code and validate response data */ + + *len = get_ca_object_length(&RspFrm); + memcpy(mmi_object, &RspFrm.operand[get_ca_object_pos(&RspFrm)], *len); return 0; } diff --git a/drivers/media/dvb/firesat/avc_api.h b/drivers/media/dvb/firesat/avc_api.h index 66f419a6f7c6..9d2efd8ff17b 100644 --- a/drivers/media/dvb/firesat/avc_api.h +++ b/drivers/media/dvb/firesat/avc_api.h @@ -26,15 +26,6 @@ **************************************************************/ #define LIST_MANAGEMENT_ONLY 0x03 -/************************************************************* - FCP Address range -**************************************************************/ - -#define RESPONSE_REGISTER 0xFFFFF0000D00ULL -#define COMMAND_REGISTER 0xFFFFF0000B00ULL -#define PCR_BASE_ADDRESS 0xFFFFF0000900ULL - - /************************************************************ definition of structures *************************************************************/ @@ -413,34 +404,29 @@ struct dvb_diseqc_master_cmd; struct dvb_frontend_parameters; struct firesat; -int AVCWrite(struct firesat *firesat, const AVCCmdFrm *CmdFrm, - AVCRspFrm *RspFrm); -int AVCRecv(struct firesat *firesat, u8 *data, size_t length); +int avc_recv(struct firesat *firesat, u8 *data, size_t length); int AVCTuner_DSIT(struct firesat *firesat, int Source_Plug, struct dvb_frontend_parameters *params, __u8 *status); -int AVCTunerStatus(struct firesat *firesat, +int avc_tuner_status(struct firesat *firesat, ANTENNA_INPUT_INFO *antenna_input_info); -int AVCTuner_DSD(struct firesat *firesat, - struct dvb_frontend_parameters *params, __u8 *status); -int AVCTuner_SetPIDs(struct firesat *firesat, unsigned char pidc, u16 pid[]); -int AVCTuner_GetTS(struct firesat *firesat); - -int AVCIdentifySubunit(struct firesat *firesat); -int AVCLNBControl(struct firesat *firesat, char voltage, char burst, +int avc_tuner_dsd(struct firesat *firesat, + struct dvb_frontend_parameters *params); +int avc_tuner_set_pids(struct firesat *firesat, unsigned char pidc, u16 pid[]); +int avc_tuner_get_ts(struct firesat *firesat); +int avc_identify_subunit(struct firesat *firesat); +int avc_lnb_control(struct firesat *firesat, char voltage, char burst, char conttone, char nrdiseq, struct dvb_diseqc_master_cmd *diseqcmd); -int AVCSubUnitInfo(struct firesat *firesat, char *subunitcount); void avc_remote_ctrl_work(struct work_struct *work); -int AVCRegisterRemoteControl(struct firesat *firesat); -int AVCTuner_Host2Ca(struct firesat *firesat); -int avc_ca_app_info(struct firesat *firesat, char *app_info, int *length); -int avc_ca_info(struct firesat *firesat, char *app_info, int *length); +int avc_register_remote_control(struct firesat *firesat); +int avc_ca_app_info(struct firesat *firesat, char *app_info, unsigned int *len); +int avc_ca_info(struct firesat *firesat, char *app_info, unsigned int *len); int avc_ca_reset(struct firesat *firesat); int avc_ca_pmt(struct firesat *firesat, char *app_info, int length); int avc_ca_get_time_date(struct firesat *firesat, int *interval); int avc_ca_enter_menu(struct firesat *firesat); -int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, int *length); +int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, unsigned int *len); #endif /* _AVC_API_H */ diff --git a/drivers/media/dvb/firesat/cmp.c b/drivers/media/dvb/firesat/cmp.c index d1bafba615e4..8e98b814e430 100644 --- a/drivers/media/dvb/firesat/cmp.c +++ b/drivers/media/dvb/firesat/cmp.c @@ -10,37 +10,21 @@ * the License, or (at your option) any later version. */ +#include <linux/device.h> #include <linux/kernel.h> #include <linux/mutex.h> #include <linux/types.h> -#include <hosts.h> +#include <asm/byteorder.h> + #include <ieee1394.h> -#include <ieee1394_core.h> -#include <ieee1394_transactions.h> #include <nodemgr.h> #include "avc_api.h" #include "cmp.h" #include "firesat.h" -typedef struct _OPCR -{ - __u8 PTPConnCount : 6 ; // Point to point connect. counter - __u8 BrConnCount : 1 ; // Broadcast connection counter - __u8 OnLine : 1 ; // On Line - - __u8 ChNr : 6 ; // Channel number - __u8 Res : 2 ; // Reserved - - __u8 PayloadHi : 2 ; // Payoad high bits - __u8 OvhdID : 4 ; // Overhead ID - __u8 DataRate : 2 ; // Data Rate - - __u8 PayloadLo ; // Payoad low byte -} OPCR ; - -#define FIRESAT_SPEED IEEE1394_SPEED_400 +#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL static int cmp_read(struct firesat *firesat, void *buf, u64 addr, size_t len) { @@ -49,151 +33,139 @@ static int cmp_read(struct firesat *firesat, void *buf, u64 addr, size_t len) if (mutex_lock_interruptible(&firesat->avc_mutex)) return -EINTR; - ret = hpsb_read(firesat->host, firesat->nodeentry->nodeid, - firesat->nodeentry->generation, addr, buf, len); + ret = hpsb_node_read(firesat->ud->ne, addr, buf, len); + if (ret < 0) + dev_err(&firesat->ud->device, "CMP: read I/O error\n"); mutex_unlock(&firesat->avc_mutex); return ret; } -static int cmp_lock(struct firesat *firesat, quadlet_t *data, u64 addr, - quadlet_t arg, int ext_tcode) +static int cmp_lock(struct firesat *firesat, void *data, u64 addr, __be32 arg, + int ext_tcode) { int ret; if (mutex_lock_interruptible(&firesat->avc_mutex)) return -EINTR; - ret = hpsb_lock(firesat->host, firesat->nodeentry->nodeid, - firesat->nodeentry->generation, - addr, ext_tcode, data, arg); + ret = hpsb_node_lock(firesat->ud->ne, addr, ext_tcode, data, + (__force quadlet_t)arg); + if (ret < 0) + dev_err(&firesat->ud->device, "CMP: lock I/O error\n"); mutex_unlock(&firesat->avc_mutex); return ret; } -//try establishing a point-to-point connection (may be interrupted by a busreset -int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, int iso_channel) { - unsigned int BWU; //bandwidth to allocate +static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) +{ + return (be32_to_cpu(opcr) >> shift) & mask; +} + +static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) +{ + *opcr &= ~cpu_to_be32(mask << shift); + *opcr |= cpu_to_be32((value & mask) << shift); +} - quadlet_t old_oPCR,test_oPCR = 0x0; - u64 oPCR_address=0xfffff0000904ull+(output_plug << 2); - int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4); +#define get_opcr_online(v) get_opcr((v), 0x1, 31) +#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) +#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) -/* printk(KERN_INFO "%s: nodeid = %d\n",__func__,firesat->nodeentry->nodeid); */ +#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) +#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) +#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) +#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) - if (result < 0) { - printk("%s: cannot read oPCR\n", __func__); - return result; - } else { -/* printk(KERN_INFO "%s: oPCR = %08x\n",__func__,test_oPCR); */ - do { - OPCR *hilf= (OPCR*) &test_oPCR; - - if (!hilf->OnLine) { - printk("%s: Output offline; oPCR: %08x\n", __func__, test_oPCR); - return -EBUSY; - } else { - quadlet_t new_oPCR; - - old_oPCR=test_oPCR; - if (hilf->PTPConnCount) { - if (hilf->ChNr != iso_channel) { - printk("%s: Output plug has already connection on channel %u; cannot change it to channel %u\n",__func__,hilf->ChNr,iso_channel); - return -EBUSY; - } else - printk(KERN_INFO "%s: Overlaying existing connection; connection counter was: %u\n",__func__, hilf->PTPConnCount); - BWU=0; //we allocate no bandwidth (is this necessary?) - } else { - hilf->ChNr=iso_channel; - hilf->DataRate=FIRESAT_SPEED; - - hilf->OvhdID=0; //FIXME: that is for worst case -> optimize - BWU=hilf->OvhdID?hilf->OvhdID*32:512; - BWU += (hilf->PayloadLo + (hilf->PayloadHi << 8) +3) * (2 << (3-hilf->DataRate)); -/* if (allocate_1394_resources(iso_channel,BWU)) - { - cout << "Allocation of resources failed\n"; - return -2; - }*/ - } - - hilf->PTPConnCount++; - new_oPCR=test_oPCR; -/* printk(KERN_INFO "%s: trying compare_swap...\n",__func__); */ -/* printk(KERN_INFO "%s: oPCR_old: %08x, oPCR_new: %08x\n",__func__, old_oPCR, new_oPCR); */ - result=cmp_lock(firesat, &test_oPCR, oPCR_address, old_oPCR, 2); - - if (result < 0) { - printk("%s: cannot compare_swap oPCR\n",__func__); - return result; - } - if ((old_oPCR != test_oPCR) && (!((OPCR*) &old_oPCR)->PTPConnCount)) - { - printk("%s: change of oPCR failed -> freeing resources\n",__func__); -// hilf= (OPCR*) &new_oPCR; -// unsigned int BWU=hilf->OvhdID?hilf->OvhdID*32:512; -// BWU += (hilf->Payload+3) * (2 << (3-hilf->DataRate)); -/* if (deallocate_1394_resources(iso_channel,BWU)) - { - - cout << "Deallocation of resources failed\n"; - return -3; - }*/ - } - } +int cmp_establish_pp_connection(struct firesat *firesat, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + int ret; + + ret = cmp_read(firesat, &opcr, opcr_address, 4); + if (ret < 0) + return ret; + +repeat: + if (!get_opcr_online(opcr)) { + dev_err(&firesat->ud->device, "CMP: output offline\n"); + return -EBUSY; + } + + old_opcr = opcr; + + if (get_opcr_p2p_connections(opcr)) { + if (get_opcr_channel(opcr) != channel) { + dev_err(&firesat->ud->device, + "CMP: cannot change channel\n"); + return -EBUSY; } - while (old_oPCR != test_oPCR); + dev_info(&firesat->ud->device, + "CMP: overlaying existing connection\n"); + + /* We don't allocate isochronous resources. */ + } else { + set_opcr_channel(&opcr, channel); + set_opcr_data_rate(&opcr, IEEE1394_SPEED_400); + + /* FIXME: this is for the worst case - optimize */ + set_opcr_overhead_id(&opcr, 0); + + /* FIXME: allocate isochronous channel and bandwidth at IRM */ } + + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); + + ret = cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2); + if (ret < 0) + return ret; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections > 0, + * deallocate isochronous channel and bandwidth at IRM + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + return -EBUSY; + } + return 0; } -//try breaking a point-to-point connection (may be interrupted by a busreset -int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug,int iso_channel) { - quadlet_t old_oPCR,test_oPCR; +void cmp_break_pp_connection(struct firesat *firesat, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + + if (cmp_read(firesat, &opcr, opcr_address, 4) < 0) + return; + +repeat: + if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || + get_opcr_channel(opcr) != channel) { + dev_err(&firesat->ud->device, "CMP: no connection to break\n"); + return; + } + + old_opcr = opcr; + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); - u64 oPCR_address=0xfffff0000904ull+(output_plug << 2); - int result=cmp_read(firesat, &test_oPCR, oPCR_address, 4); + if (cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2) < 0) + return; -/* printk(KERN_INFO "%s\n",__func__); */ + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last + * owner, deallocate isochronous channel and bandwidth at IRM + */ - if (result < 0) { - printk("%s: cannot read oPCR\n", __func__); - return result; - } else { - do { - OPCR *hilf= (OPCR*) &test_oPCR; - - if (!hilf->OnLine || !hilf->PTPConnCount || hilf->ChNr != iso_channel) { - printk("%s: Output plug does not have PtP-connection on that channel; oPCR: %08x\n", __func__, test_oPCR); - return -EINVAL; - } else { - quadlet_t new_oPCR; - old_oPCR=test_oPCR; - hilf->PTPConnCount--; - new_oPCR=test_oPCR; - -// printk(KERN_INFO "%s: trying compare_swap...\n", __func__); - result=cmp_lock(firesat, &test_oPCR, oPCR_address, old_oPCR, 2); - if (result < 0) { - printk("%s: cannot compare_swap oPCR\n",__func__); - return result; - } - } - - } while (old_oPCR != test_oPCR); - -/* hilf = (OPCR*) &old_oPCR; - if (hilf->PTPConnCount == 1) { // if we were the last owner of this connection - cout << "deallocating 1394 resources\n"; - unsigned int BWU=hilf->OvhdID?hilf->OvhdID*32:512; - BWU += (hilf->PayloadLo + (hilf->PayloadHi << 8) +3) * (2 << (3-hilf->DataRate)); - if (deallocate_1394_resources(iso_channel,BWU)) - { - cout << "Deallocation of resources failed\n"; - return -3; - } - }*/ - } - return 0; + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + } } diff --git a/drivers/media/dvb/firesat/cmp.h b/drivers/media/dvb/firesat/cmp.h index 600d5784dc72..d92f6c7fb5d5 100644 --- a/drivers/media/dvb/firesat/cmp.h +++ b/drivers/media/dvb/firesat/cmp.h @@ -3,9 +3,7 @@ struct firesat; -int try_CMPEstablishPPconnection(struct firesat *firesat, int output_plug, - int iso_channel); -int try_CMPBreakPPconnection(struct firesat *firesat, int output_plug, - int iso_channel); +int cmp_establish_pp_connection(struct firesat *firesat, int plug, int channel); +void cmp_break_pp_connection(struct firesat *firesat, int plug, int channel); #endif /* _CMP_H */ diff --git a/drivers/media/dvb/firesat/firesat-ci.c b/drivers/media/dvb/firesat/firesat-ci.c index 3ef25cc4bfdb..0deb47eefa10 100644 --- a/drivers/media/dvb/firesat/firesat-ci.c +++ b/drivers/media/dvb/firesat/firesat-ci.c @@ -20,29 +20,18 @@ #include "firesat.h" #include "firesat-ci.h" -static unsigned int ca_debug = 0; -module_param(ca_debug, int, 0644); -MODULE_PARM_DESC(ca_debug, "debug logging of ca system, default is 0 (no)"); - static int firesat_ca_ready(ANTENNA_INPUT_INFO *info) { - if (ca_debug != 0) - printk("%s: CaMmi=%d, CaInit=%d, CaError=%d, CaDvb=%d, " - "CaModule=%d, CaAppInfo=%d, CaDateTime=%d, " - "CaPmt=%d\n", __func__, info->CaMmi, - info->CaInitializationStatus, info->CaErrorFlag, - info->CaDvbFlag, info->CaModulePresentStatus, - info->CaApplicationInfo, - info->CaDateTimeRequest, info->CaPmtReply); return info->CaInitializationStatus == 1 && - info->CaErrorFlag == 0 && - info->CaDvbFlag == 1 && - info->CaModulePresentStatus == 1; + info->CaErrorFlag == 0 && + info->CaDvbFlag == 1 && + info->CaModulePresentStatus == 1; } static int firesat_get_ca_flags(ANTENNA_INPUT_INFO *info) { int flags = 0; + if (info->CaModulePresentStatus == 1) flags |= CA_CI_MODULE_PRESENT; if (info->CaInitializationStatus == 1 && @@ -54,103 +43,63 @@ static int firesat_get_ca_flags(ANTENNA_INPUT_INFO *info) static int firesat_ca_reset(struct firesat *firesat) { - if (ca_debug) - printk(KERN_INFO "%s: ioctl CA_RESET\n", __func__); - if (avc_ca_reset(firesat)) - return -EFAULT; - return 0; + return avc_ca_reset(firesat) ? -EFAULT : 0; } -static int firesat_ca_get_caps(struct firesat *firesat, void *arg) +static int firesat_ca_get_caps(void *arg) { - struct ca_caps *cap_p = (struct ca_caps*)arg; - int err = 0; - - cap_p->slot_num = 1; - cap_p->slot_type = CA_CI; - cap_p->descr_num = 1; - cap_p->descr_type = CA_ECD; - if (ca_debug) - printk(KERN_INFO "%s: ioctl CA_GET_CAP\n", __func__); - return err; + struct ca_caps *cap = arg; + + cap->slot_num = 1; + cap->slot_type = CA_CI; + cap->descr_num = 1; + cap->descr_type = CA_ECD; + return 0; } static int firesat_ca_get_slot_info(struct firesat *firesat, void *arg) { ANTENNA_INPUT_INFO info; - struct ca_slot_info *slot_p = (struct ca_slot_info*)arg; + struct ca_slot_info *slot = arg; - if (ca_debug) - printk(KERN_INFO "%s: ioctl CA_GET_SLOT_INFO on slot %d.\n", - __func__, slot_p->num); - if (AVCTunerStatus(firesat, &info)) + if (avc_tuner_status(firesat, &info)) return -EFAULT; - if (slot_p->num == 0) { - slot_p->type = CA_CI; - slot_p->flags = firesat_get_ca_flags(&info); - } - else { + if (slot->num != 0) return -EFAULT; - } + + slot->type = CA_CI; + slot->flags = firesat_get_ca_flags(&info); return 0; } static int firesat_ca_app_info(struct firesat *firesat, void *arg) { - struct ca_msg *reply_p = (struct ca_msg*)arg; - int i; + struct ca_msg *reply = arg; - if (avc_ca_app_info(firesat, reply_p->msg, &reply_p->length)) - return -EFAULT; - if (ca_debug) { - printk(KERN_INFO "%s: Creating TAG_APP_INFO message:", - __func__); - for (i = 0; i < reply_p->length; i++) - printk("0x%02X, ", (unsigned char)reply_p->msg[i]); - printk("\n"); - } - return 0; + return + avc_ca_app_info(firesat, reply->msg, &reply->length) ? -EFAULT : 0; } static int firesat_ca_info(struct firesat *firesat, void *arg) { - struct ca_msg *reply_p = (struct ca_msg*)arg; - int i; + struct ca_msg *reply = arg; - if (avc_ca_info(firesat, reply_p->msg, &reply_p->length)) - return -EFAULT; - if (ca_debug) { - printk(KERN_INFO "%s: Creating TAG_CA_INFO message:", - __func__); - for (i = 0; i < reply_p->length; i++) - printk("0x%02X, ", (unsigned char)reply_p->msg[i]); - printk("\n"); - } - return 0; + return avc_ca_info(firesat, reply->msg, &reply->length) ? -EFAULT : 0; } static int firesat_ca_get_mmi(struct firesat *firesat, void *arg) { - struct ca_msg *reply_p = (struct ca_msg*)arg; - int i; + struct ca_msg *reply = arg; - if (avc_ca_get_mmi(firesat, reply_p->msg, &reply_p->length)) - return -EFAULT; - if (ca_debug) { - printk(KERN_INFO "%s: Creating MMI reply INFO message:", - __func__); - for (i = 0; i < reply_p->length; i++) - printk("0x%02X, ", (unsigned char)reply_p->msg[i]); - printk("\n"); - } - return 0; + return + avc_ca_get_mmi(firesat, reply->msg, &reply->length) ? -EFAULT : 0; } static int firesat_ca_get_msg(struct firesat *firesat, void *arg) { - int err; ANTENNA_INPUT_INFO info; + int err; switch (firesat->ca_last_command) { case TAG_APP_INFO_ENQUIRY: @@ -160,11 +109,10 @@ static int firesat_ca_get_msg(struct firesat *firesat, void *arg) err = firesat_ca_info(firesat, arg); break; default: - if (AVCTunerStatus(firesat, &info)) + if (avc_tuner_status(firesat, &info)) err = -EFAULT; - else if (info.CaMmi == 1) { + else if (info.CaMmi == 1) err = firesat_ca_get_mmi(firesat, arg); - } else { printk(KERN_INFO "%s: Unhandled message 0x%08X\n", __func__, firesat->ca_last_command); @@ -177,51 +125,39 @@ static int firesat_ca_get_msg(struct firesat *firesat, void *arg) static int firesat_ca_pmt(struct firesat *firesat, void *arg) { - struct ca_msg *msg_p = (struct ca_msg*)arg; + struct ca_msg *msg = arg; int data_pos; - if (msg_p->msg[3] & 0x80) - data_pos = (msg_p->msg[4] && 0x7F) + 4; + if (msg->msg[3] & 0x80) + data_pos = (msg->msg[4] && 0x7F) + 4; else data_pos = 4; - if (avc_ca_pmt(firesat, &msg_p->msg[data_pos], - msg_p->length - data_pos)) - return -EFAULT; - return 0; + + return avc_ca_pmt(firesat, &msg->msg[data_pos], + msg->length - data_pos) ? -EFAULT : 0; } static int firesat_ca_send_msg(struct firesat *firesat, void *arg) { + struct ca_msg *msg = arg; int err; - struct ca_msg *msg_p = (struct ca_msg*)arg; - // Do we need a semaphore for this? + /* Do we need a semaphore for this? */ firesat->ca_last_command = - (msg_p->msg[0] << 16) + (msg_p->msg[1] << 8) + msg_p->msg[2]; + (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; switch (firesat->ca_last_command) { case TAG_CA_PMT: - if (ca_debug != 0) - printk(KERN_INFO "%s: Message received: TAG_CA_PMT\n", - __func__); err = firesat_ca_pmt(firesat, arg); break; case TAG_APP_INFO_ENQUIRY: - // This is all handled in ca_get_msg - if (ca_debug != 0) - printk(KERN_INFO "%s: Message received: " - "TAG_APP_INFO_ENQUIRY\n", __func__); + /* handled in ca_get_msg */ err = 0; break; case TAG_CA_INFO_ENQUIRY: - // This is all handled in ca_get_msg - if (ca_debug != 0) - printk(KERN_INFO "%s: Message received: " - "TAG_CA_APP_INFO_ENQUIRY\n", __func__); + /* handled in ca_get_msg */ err = 0; break; case TAG_ENTER_MENU: - if (ca_debug != 0) - printk(KERN_INFO "%s: Entering CA menu.\n", __func__); err = avc_ca_enter_menu(firesat); break; default: @@ -235,17 +171,17 @@ static int firesat_ca_send_msg(struct firesat *firesat, void *arg) static int firesat_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct dvb_device* dvbdev = (struct dvb_device*) file->private_data; + struct dvb_device *dvbdev = file->private_data; struct firesat *firesat = dvbdev->priv; - int err; ANTENNA_INPUT_INFO info; + int err; switch(cmd) { case CA_RESET: err = firesat_ca_reset(firesat); break; case CA_GET_CAP: - err = firesat_ca_get_caps(firesat, arg); + err = firesat_ca_get_caps(arg); break; case CA_GET_SLOT_INFO: err = firesat_ca_get_slot_info(firesat, arg); @@ -262,90 +198,52 @@ static int firesat_ca_ioctl(struct inode *inode, struct file *file, err = -EOPNOTSUPP; } - if (AVCTunerStatus(firesat, &info)) - return err; - - firesat_ca_ready(&info); + /* FIXME Is this necessary? */ + avc_tuner_status(firesat, &info); return err; } -static int firesat_get_date_time_request(struct firesat *firesat) -{ - if (ca_debug) - printk(KERN_INFO "%s: Retrieving Time/Date request\n", - __func__); - if (avc_ca_get_time_date(firesat, &firesat->ca_time_interval)) - return -EFAULT; - if (ca_debug) - printk(KERN_INFO "%s: Time/Date interval is %d\n", - __func__, firesat->ca_time_interval); - - return 0; -} - -static int firesat_ca_io_open(struct inode *inode, struct file *file) -{ - if (ca_debug != 0) - printk(KERN_INFO "%s\n",__func__); - return dvb_generic_open(inode, file); -} - -static int firesat_ca_io_release(struct inode *inode, struct file *file) -{ - if (ca_debug != 0) - printk(KERN_INFO "%s\n",__func__); - return dvb_generic_release(inode, file); -} - static unsigned int firesat_ca_io_poll(struct file *file, poll_table *wait) { - if (ca_debug != 0) - printk(KERN_INFO "%s\n",__func__); return POLLIN; } static struct file_operations firesat_ca_fops = { - .owner = THIS_MODULE, - .read = NULL, // There is no low level read anymore - .write = NULL, // There is no low level write anymore - .ioctl = dvb_generic_ioctl, - .open = firesat_ca_io_open, - .release = firesat_ca_io_release, - .poll = firesat_ca_io_poll, + .owner = THIS_MODULE, + .ioctl = dvb_generic_ioctl, + .open = dvb_generic_open, + .release = dvb_generic_release, + .poll = firesat_ca_io_poll, }; static struct dvb_device firesat_ca = { - .priv = NULL, - .users = 1, - .readers = 1, - .writers = 1, - .fops = &firesat_ca_fops, - .kernel_ioctl = firesat_ca_ioctl, + .users = 1, + .readers = 1, + .writers = 1, + .fops = &firesat_ca_fops, + .kernel_ioctl = firesat_ca_ioctl, }; -int firesat_ca_init(struct firesat *firesat) +int firesat_ca_register(struct firesat *firesat) { - int err; ANTENNA_INPUT_INFO info; + int err; - if (AVCTunerStatus(firesat, &info)) + if (avc_tuner_status(firesat, &info)) return -EINVAL; - if (firesat_ca_ready(&info)) { - err = dvb_register_device(firesat->adapter, - &firesat->cadev, - &firesat_ca, firesat, - DVB_DEVICE_CA); - - if (info.CaApplicationInfo == 0) - printk(KERN_ERR "%s: CaApplicationInfo is not set.\n", - __func__); - if (info.CaDateTimeRequest == 1) - firesat_get_date_time_request(firesat); - } - else - err = -EFAULT; + if (!firesat_ca_ready(&info)) + return -EFAULT; + + err = dvb_register_device(&firesat->adapter, &firesat->cadev, + &firesat_ca, firesat, DVB_DEVICE_CA); + + if (info.CaApplicationInfo == 0) + printk(KERN_ERR "%s: CaApplicationInfo is not set.\n", + __func__); + if (info.CaDateTimeRequest == 1) + avc_ca_get_time_date(firesat, &firesat->ca_time_interval); return err; } @@ -353,5 +251,5 @@ int firesat_ca_init(struct firesat *firesat) void firesat_ca_release(struct firesat *firesat) { if (firesat->cadev) - dvb_unregister_device(firesat->cadev); + dvb_unregister_device(firesat->cadev); } diff --git a/drivers/media/dvb/firesat/firesat-ci.h b/drivers/media/dvb/firesat/firesat-ci.h index 04fe4061c778..9c68cd2246a7 100644 --- a/drivers/media/dvb/firesat/firesat-ci.h +++ b/drivers/media/dvb/firesat/firesat-ci.h @@ -3,7 +3,7 @@ struct firesat; -int firesat_ca_init(struct firesat *firesat); +int firesat_ca_register(struct firesat *firesat); void firesat_ca_release(struct firesat *firesat); #endif /* _FIREDTV_CI_H */ diff --git a/drivers/media/dvb/firesat/firesat-rc.c b/drivers/media/dvb/firesat/firesat-rc.c index d3e08f9fe9f7..5f9de142ee3e 100644 --- a/drivers/media/dvb/firesat/firesat-rc.c +++ b/drivers/media/dvb/firesat/firesat-rc.c @@ -12,9 +12,11 @@ #include <linux/bitops.h> #include <linux/input.h> #include <linux/kernel.h> +#include <linux/string.h> #include <linux/types.h> #include "firesat-rc.h" +#include "firesat.h" /* fixed table with older keycodes, geared towards MythTV */ const static u16 oldtable[] = { @@ -61,7 +63,7 @@ const static u16 oldtable[] = { }; /* user-modifiable table for a remote as sold in 2008 */ -static u16 keytable[] = { +const static u16 keytable[] = { /* code from device: 0x0300...0x031f */ @@ -123,19 +125,24 @@ static u16 keytable[] = { [0x34] = KEY_EXIT, }; -static struct input_dev *idev; - -int firesat_register_rc(void) +int firesat_register_rc(struct firesat *firesat, struct device *dev) { + struct input_dev *idev; int i, err; idev = input_allocate_device(); if (!idev) return -ENOMEM; + firesat->remote_ctrl_dev = idev; idev->name = "FireDTV remote control"; + idev->dev.parent = dev; idev->evbit[0] = BIT_MASK(EV_KEY); - idev->keycode = keytable; + idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); + if (!idev->keycode) { + err = -ENOMEM; + goto fail; + } idev->keycodesize = sizeof(keytable[0]); idev->keycodemax = ARRAY_SIZE(keytable); @@ -144,22 +151,31 @@ int firesat_register_rc(void) err = input_register_device(idev); if (err) - input_free_device(idev); + goto fail_free_keymap; + return 0; + +fail_free_keymap: + kfree(idev->keycode); +fail: + input_free_device(idev); return err; } -void firesat_unregister_rc(void) +void firesat_unregister_rc(struct firesat *firesat) { - input_unregister_device(idev); + kfree(firesat->remote_ctrl_dev->keycode); + input_unregister_device(firesat->remote_ctrl_dev); } -void firesat_handle_rc(unsigned int code) +void firesat_handle_rc(struct firesat *firesat, unsigned int code) { + u16 *keycode = firesat->remote_ctrl_dev->keycode; + if (code >= 0x0300 && code <= 0x031f) - code = keytable[code - 0x0300]; + code = keycode[code - 0x0300]; else if (code >= 0x0340 && code <= 0x0354) - code = keytable[code - 0x0320]; + code = keycode[code - 0x0320]; else if (code >= 0x4501 && code <= 0x451f) code = oldtable[code - 0x4501]; else if (code >= 0x4540 && code <= 0x4542) @@ -170,6 +186,6 @@ void firesat_handle_rc(unsigned int code) return; } - input_report_key(idev, code, 1); - input_report_key(idev, code, 0); + input_report_key(firesat->remote_ctrl_dev, code, 1); + input_report_key(firesat->remote_ctrl_dev, code, 0); } diff --git a/drivers/media/dvb/firesat/firesat-rc.h b/drivers/media/dvb/firesat/firesat-rc.h index 81f4fdec60f1..12c1c5c28b36 100644 --- a/drivers/media/dvb/firesat/firesat-rc.h +++ b/drivers/media/dvb/firesat/firesat-rc.h @@ -1,8 +1,11 @@ #ifndef _FIREDTV_RC_H #define _FIREDTV_RC_H -int firesat_register_rc(void); -void firesat_unregister_rc(void); -void firesat_handle_rc(unsigned int code); +struct firesat; +struct device; + +int firesat_register_rc(struct firesat *firesat, struct device *dev); +void firesat_unregister_rc(struct firesat *firesat); +void firesat_handle_rc(struct firesat *firesat, unsigned int code); #endif /* _FIREDTV_RC_H */ diff --git a/drivers/media/dvb/firesat/firesat.h b/drivers/media/dvb/firesat/firesat.h index 5f0de88e41a6..51f64c0afcdb 100644 --- a/drivers/media/dvb/firesat/firesat.h +++ b/drivers/media/dvb/firesat/firesat.h @@ -21,11 +21,11 @@ #include <linux/types.h> #include <linux/wait.h> #include <linux/workqueue.h> -#include <asm/atomic.h> #include <demux.h> #include <dmxdev.h> #include <dvb_demux.h> +#include <dvb_frontend.h> #include <dvb_net.h> #include <dvbdev.h> @@ -127,50 +127,35 @@ enum model_type { FireSAT_DVB_S2 = 4, }; -struct hpsb_host; +struct input_dev; struct hpsb_iso; -struct node_entry; +struct unit_directory; struct firesat { - struct dvb_demux dvb_demux; - - /* DVB bits */ - struct dvb_adapter *adapter; - struct dmxdev dmxdev; - struct dvb_demux demux; - struct dmx_frontend frontend; - struct dvb_net dvbnet; - struct dvb_frontend_info *frontend_info; - struct dvb_frontend *fe; - - struct dvb_device *cadev; - int ca_last_command; - int ca_time_interval; - - struct mutex avc_mutex; - wait_queue_head_t avc_wait; - atomic_t avc_reply_received; - struct work_struct remote_ctrl_work; + struct dvb_adapter adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dmx_frontend frontend; + struct dvb_net dvbnet; + struct dvb_frontend fe; + + struct dvb_device *cadev; + int ca_last_command; + int ca_time_interval; + + struct mutex avc_mutex; + wait_queue_head_t avc_wait; + bool avc_reply_received; + struct work_struct remote_ctrl_work; + struct input_dev *remote_ctrl_dev; struct firesat_channel { - struct firesat *firesat; - struct dvb_demux_feed *dvbdmxfeed; - - int active; - int id; + bool active; int pid; - int type; /* 1 - TS, 2 - Filter */ } channel[16]; - struct mutex demux_mutex; - - /* needed by avc_api */ - void *respfrm; - int resp_length; + struct mutex demux_mutex; - struct hpsb_host *host; - u64 guid; /* GUID of this node */ - u32 guid_vendor_id; /* Top 24bits of guid */ - struct node_entry *nodeentry; + struct unit_directory *ud; enum model_type type; char subunit; @@ -181,6 +166,10 @@ struct firesat { struct hpsb_iso *iso_handle; struct list_head list; + + /* needed by avc_api */ + int resp_length; + u8 respfrm[512]; }; struct firewireheader { @@ -226,11 +215,10 @@ struct device; /* firesat_dvb.c */ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed); int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed); -int firesat_dvbdev_init(struct firesat *firesat, struct device *dev, - struct dvb_frontend *fe); +int firesat_dvbdev_init(struct firesat *firesat, struct device *dev); /* firesat_fe.c */ -int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe); +void firesat_frontend_init(struct firesat *firesat); /* firesat_iso.c */ int setup_iso_channel(struct firesat *firesat); diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c index a13fbe6b3a3c..82bd9786571d 100644 --- a/drivers/media/dvb/firesat/firesat_1394.c +++ b/drivers/media/dvb/firesat/firesat_1394.c @@ -21,7 +21,6 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <linux/types.h> -#include <asm/atomic.h> #include <dmxdev.h> #include <dvb_demux.h> @@ -40,40 +39,54 @@ #include "firesat-ci.h" #include "firesat-rc.h" -#define FIRESAT_Vendor_ID 0x001287 +#define MATCH_FLAGS IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ + IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION +#define DIGITAL_EVERYWHERE_OUI 0x001287 static struct ieee1394_device_id firesat_id_table[] = { { /* FloppyDTV S/CI and FloppyDTV S2 */ - .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, - .model_id = 0x000024, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000024, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, },{ /* FloppyDTV T/CI */ - .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, - .model_id = 0x000025, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000025, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, },{ /* FloppyDTV C/CI */ - .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, - .model_id = 0x000026, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000026, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, },{ /* FireDTV S/CI and FloppyDTV S2 */ - .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, - .model_id = 0x000034, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000034, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, },{ /* FireDTV T/CI */ - .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, - .model_id = 0x000035, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000035, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, },{ /* FireDTV C/CI */ - .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, - .model_id = 0x000036, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000036, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, }, { } }; @@ -98,8 +111,8 @@ static void fcp_request(struct hpsb_host *host, spin_lock_irqsave(&firesat_list_lock, flags); list_for_each_entry(firesat_entry,&firesat_list,list) { - if (firesat_entry->host == host && - firesat_entry->nodeentry->nodeid == nodeid && + if (firesat_entry->ud->ne->host == host && + firesat_entry->ud->ne->nodeid == nodeid && (firesat_entry->subunit == (data[1]&0x7) || (firesat_entry->subunit == 0 && (data[1]&0x7) == 0x7))) { @@ -110,12 +123,8 @@ static void fcp_request(struct hpsb_host *host, spin_unlock_irqrestore(&firesat_list_lock, flags); if (firesat) - AVCRecv(firesat,data,length); - else - printk("%s: received fcp request from unknown source, ignored\n", __func__); + avc_recv(firesat, data, length); } - else - printk("%s: received invalid fcp request, ignored\n", __func__); } const char *firedtv_model_names[] = { @@ -128,175 +137,108 @@ const char *firedtv_model_names[] = { static int firesat_probe(struct device *dev) { - struct unit_directory *ud = container_of(dev, struct unit_directory, device); + struct unit_directory *ud = + container_of(dev, struct unit_directory, device); struct firesat *firesat; - struct dvb_frontend *fe; unsigned long flags; - unsigned char subunitcount = 0xff, subunit; - struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL); int kv_len; + void *kv_str; int i; - char *kv_buf; + int err = -ENOMEM; - if (!firesats) { - printk("%s: couldn't allocate memory.\n", __func__); + firesat = kzalloc(sizeof(*firesat), GFP_KERNEL); + if (!firesat) return -ENOMEM; - } - -// printk(KERN_INFO "FireSAT: Detected device with GUID %08lx%04lx%04lx\n",(unsigned long)((ud->ne->guid)>>32),(unsigned long)(ud->ne->guid & 0xFFFF),(unsigned long)ud->ne->guid_vendor_id); - printk(KERN_INFO "%s: loading device\n", __func__); - - firesats[0] = NULL; - firesats[1] = NULL; - - ud->device.driver_data = firesats; - - for (subunit = 0; subunit < subunitcount; subunit++) { - - if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) || - !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) { - - printk("%s: couldn't allocate memory.\n", __func__); - kfree(firesats); - return -ENOMEM; - } - - memset(firesat, 0, sizeof (struct firesat)); - - firesat->host = ud->ne->host; - firesat->guid = ud->ne->guid; - firesat->guid_vendor_id = ud->ne->guid_vendor_id; - firesat->nodeentry = ud->ne; - firesat->isochannel = -1; - firesat->tone = 0xff; - firesat->voltage = 0xff; - firesat->fe = fe; - - if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) { - printk("%s: couldn't allocate memory.\n", __func__); - kfree(firesat); - return -ENOMEM; - } - - mutex_init(&firesat->avc_mutex); - init_waitqueue_head(&firesat->avc_wait); - atomic_set(&firesat->avc_reply_received, 1); - mutex_init(&firesat->demux_mutex); - INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work); - - spin_lock_irqsave(&firesat_list_lock, flags); - INIT_LIST_HEAD(&firesat->list); - list_add_tail(&firesat->list, &firesat_list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - - if (subunit == 0) { - firesat->subunit = 0x7; // 0x7 = don't care - if (AVCSubUnitInfo(firesat, &subunitcount)) { - printk("%s: AVC subunit info command failed.\n",__func__); - spin_lock_irqsave(&firesat_list_lock, flags); - list_del(&firesat->list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - kfree(firesat); - return -EIO; - } - } - - printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount); - - firesat->subunit = subunit; - - /* Reading device model from ROM */ - kv_len = (ud->model_name_kv->value.leaf.len - 2) * - sizeof(quadlet_t); - kv_buf = kmalloc((sizeof(quadlet_t) * kv_len), GFP_KERNEL); - memcpy(kv_buf, - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv), - kv_len); - while ((kv_buf + kv_len - 1) == '\0') kv_len--; - kv_buf[kv_len++] = '\0'; - for (i = ARRAY_SIZE(firedtv_model_names); --i;) - if (strcmp(kv_buf, firedtv_model_names[i]) == 0) - break; - firesat->type = i; - kfree(kv_buf); - - if (AVCIdentifySubunit(firesat)) { - printk("%s: cannot identify subunit %d\n", __func__, subunit); - spin_lock_irqsave(&firesat_list_lock, flags); - list_del(&firesat->list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - kfree(firesat); - continue; - } + dev->driver_data = firesat; + firesat->ud = ud; + firesat->subunit = 0; + firesat->isochannel = -1; + firesat->tone = 0xff; + firesat->voltage = 0xff; + + mutex_init(&firesat->avc_mutex); + init_waitqueue_head(&firesat->avc_wait); + firesat->avc_reply_received = true; + mutex_init(&firesat->demux_mutex); + INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work); + + /* Reading device model from ROM */ + kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); + kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); + for (i = ARRAY_SIZE(firedtv_model_names); --i;) + if (strlen(firedtv_model_names[i]) <= kv_len && + strncmp(kv_str, firedtv_model_names[i], kv_len) == 0) + break; + firesat->type = i; + + /* + * Work around a bug in udev's path_id script: Use the fw-host's dev + * instead of the unit directory's dev as parent of the input device. + */ + err = firesat_register_rc(firesat, dev->parent->parent); + if (err) + goto fail_free; + + INIT_LIST_HEAD(&firesat->list); + spin_lock_irqsave(&firesat_list_lock, flags); + list_add_tail(&firesat->list, &firesat_list); + spin_unlock_irqrestore(&firesat_list_lock, flags); + + err = avc_identify_subunit(firesat); + if (err) + goto fail; -// ---- - /* FIXME: check for error return */ - firesat_dvbdev_init(firesat, dev, fe); -// ---- - firesats[subunit] = firesat; - } // loop for all tuners + err = firesat_dvbdev_init(firesat, dev); + if (err) + goto fail; - if (firesats[0]) - AVCRegisterRemoteControl(firesats[0]); + avc_register_remote_control(firesat); + return 0; - return 0; +fail: + spin_lock_irqsave(&firesat_list_lock, flags); + list_del(&firesat->list); + spin_unlock_irqrestore(&firesat_list_lock, flags); + firesat_unregister_rc(firesat); +fail_free: + kfree(firesat); + return err; } static int firesat_remove(struct device *dev) { - struct unit_directory *ud = container_of(dev, struct unit_directory, device); - struct firesat **firesats = ud->device.driver_data; - int k; + struct firesat *firesat = dev->driver_data; unsigned long flags; - if (firesats) { - for (k = 0; k < 2; k++) - if (firesats[k]) { - firesat_ca_release(firesats[k]); - - dvb_unregister_frontend(firesats[k]->fe); - dvb_net_release(&firesats[k]->dvbnet); - firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx); - firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend); - dvb_dmxdev_release(&firesats[k]->dmxdev); - dvb_dmx_release(&firesats[k]->demux); - dvb_unregister_adapter(firesats[k]->adapter); - - spin_lock_irqsave(&firesat_list_lock, flags); - list_del(&firesats[k]->list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - - cancel_work_sync(&firesats[k]->remote_ctrl_work); - - kfree(firesats[k]->fe); - kfree(firesats[k]->adapter); - kfree(firesats[k]->respfrm); - kfree(firesats[k]); - } - kfree(firesats); - } else - printk("%s: can't get firesat handle\n", __func__); + firesat_ca_release(firesat); + dvb_unregister_frontend(&firesat->fe); + dvb_net_release(&firesat->dvbnet); + firesat->demux.dmx.close(&firesat->demux.dmx); + firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, + &firesat->frontend); + dvb_dmxdev_release(&firesat->dmxdev); + dvb_dmx_release(&firesat->demux); + dvb_unregister_adapter(&firesat->adapter); + + spin_lock_irqsave(&firesat_list_lock, flags); + list_del(&firesat->list); + spin_unlock_irqrestore(&firesat_list_lock, flags); - printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id); + cancel_work_sync(&firesat->remote_ctrl_work); + firesat_unregister_rc(firesat); + kfree(firesat); return 0; } static int firesat_update(struct unit_directory *ud) { - struct firesat **firesats = ud->device.driver_data; - int k; - // loop over subunits - - for (k = 0; k < 2; k++) - if (firesats[k]) { - firesats[k]->nodeentry = ud->ne; - - if (firesats[k]->isochannel >= 0) - try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel); - } + struct firesat *firesat = ud->device.driver_data; + if (firesat->isochannel >= 0) + cmp_establish_pp_connection(firesat, firesat->subunit, + firesat->isochannel); return 0; } @@ -328,26 +270,13 @@ static int __init firesat_init(void) ret = hpsb_register_protocol(&firesat_driver); if (ret) { printk(KERN_ERR "firedtv: failed to register protocol\n"); - goto fail; - } - - ret = firesat_register_rc(); - if (ret) { - printk(KERN_ERR "firedtv: failed to register input device\n"); - goto fail_rc; + hpsb_unregister_highlevel(&firesat_highlevel); } - - return 0; -fail_rc: - hpsb_unregister_protocol(&firesat_driver); -fail: - hpsb_unregister_highlevel(&firesat_highlevel); return ret; } static void __exit firesat_exit(void) { - firesat_unregister_rc(); hpsb_unregister_protocol(&firesat_driver); hpsb_unregister_highlevel(&firesat_highlevel); } diff --git a/drivers/media/dvb/firesat/firesat_dvb.c b/drivers/media/dvb/firesat/firesat_dvb.c index e944cee19f0a..cfa3a2e8edd1 100644 --- a/drivers/media/dvb/firesat/firesat_dvb.c +++ b/drivers/media/dvb/firesat/firesat_dvb.c @@ -34,8 +34,8 @@ static struct firesat_channel *firesat_channel_allocate(struct firesat *firesat) return NULL; for (k = 0; k < 16; k++) - if (firesat->channel[k].active == 0) { - firesat->channel[k].active = 1; + if (!firesat->channel[k].active) { + firesat->channel[k].active = true; c = &firesat->channel[k]; break; } @@ -52,7 +52,7 @@ static int firesat_channel_collect(struct firesat *firesat, int *pidc, u16 pid[] return -EINTR; for (k = 0; k < 16; k++) - if (firesat->channel[k].active == 1) + if (firesat->channel[k].active) pid[l++] = firesat->channel[k].pid; mutex_unlock(&firesat->demux_mutex); @@ -68,7 +68,7 @@ static int firesat_channel_release(struct firesat *firesat, if (mutex_lock_interruptible(&firesat->demux_mutex)) return -EINTR; - channel->active = 0; + channel->active = false; mutex_unlock(&firesat->demux_mutex); return 0; @@ -81,8 +81,6 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) int pidc,k; u16 pids[16]; -// printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid); - switch (dvbdmxfeed->type) { case DMX_TYPE_TS: case DMX_TYPE_SEC: @@ -102,7 +100,7 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) case DMX_TS_PES_OTHER: //Dirty fix to keep firesat->channel pid-list up to date for(k=0;k<16;k++){ - if(firesat->channel[k].active == 0) + if (!firesat->channel[k].active) firesat->channel[k].pid = dvbdmxfeed->pid; break; @@ -124,11 +122,7 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) } dvbdmxfeed->priv = channel; - - channel->dvbdmxfeed = dvbdmxfeed; channel->pid = dvbdmxfeed->pid; - channel->type = dvbdmxfeed->type; - channel->firesat = firesat; if (firesat_channel_collect(firesat, &pidc, pids)) { firesat_channel_release(firesat, channel); @@ -136,16 +130,17 @@ int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) return -EINTR; } - if(dvbdmxfeed->pid == 8192) { - if((k = AVCTuner_GetTS(firesat))) { + if (dvbdmxfeed->pid == 8192) { + k = avc_tuner_get_ts(firesat); + if (k) { firesat_channel_release(firesat, channel); printk("%s: AVCTuner_GetTS failed with error %d\n", __func__, k); return k; } - } - else { - if((k = AVCTuner_SetPIDs(firesat, pidc, pids))) { + } else { + k = avc_tuner_set_pids(firesat, pidc, pids); + if (k) { firesat_channel_release(firesat, channel); printk("%s: AVCTuner_SetPIDs failed with error %d\n", __func__, k); @@ -164,8 +159,6 @@ int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) int k, l; u16 pids[16]; - //printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid); - if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) && (demux->dmx.frontend->source != DMX_MEMORY_FE))) { @@ -177,7 +170,7 @@ int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) return -EINVAL; demux->pids[dvbdmxfeed->pes_type] |= 0x8000; - demux->pesfilter[dvbdmxfeed->pes_type] = 0; + demux->pesfilter[dvbdmxfeed->pes_type] = NULL; } if (!(dvbdmxfeed->ts_type & TS_DECODER && @@ -191,118 +184,93 @@ int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) /* list except channel to be removed */ for (k = 0, l = 0; k < 16; k++) - if (firesat->channel[k].active == 1) { + if (firesat->channel[k].active) { if (&firesat->channel[k] != c) pids[l++] = firesat->channel[k].pid; else - firesat->channel[k].active = 0; + firesat->channel[k].active = false; } - k = AVCTuner_SetPIDs(firesat, l, pids); + k = avc_tuner_set_pids(firesat, l, pids); if (!k) - c->active = 0; + c->active = false; mutex_unlock(&firesat->demux_mutex); return k; } -int firesat_dvbdev_init(struct firesat *firesat, - struct device *dev, - struct dvb_frontend *fe) +int firesat_dvbdev_init(struct firesat *firesat, struct device *dev) { - int result; - - firesat->adapter = kmalloc(sizeof(*firesat->adapter), GFP_KERNEL); - if (!firesat->adapter) { - printk(KERN_ERR "firedtv: couldn't allocate memory\n"); - return -ENOMEM; - } - - result = DVB_REGISTER_ADAPTER(firesat->adapter, - firedtv_model_names[firesat->type], - THIS_MODULE, dev, adapter_nr); - if (result < 0) { - printk(KERN_ERR "firedtv: dvb_register_adapter failed\n"); - kfree(firesat->adapter); - return result; - } - - memset(&firesat->demux, 0, sizeof(struct dvb_demux)); - firesat->demux.dmx.capabilities = 0/*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/; - - firesat->demux.priv = (void *)firesat; - firesat->demux.filternum = 16; - firesat->demux.feednum = 16; - firesat->demux.start_feed = firesat_start_feed; - firesat->demux.stop_feed = firesat_stop_feed; - firesat->demux.write_to_decoder = NULL; + int err; - if ((result = dvb_dmx_init(&firesat->demux)) < 0) { - printk("%s: dvb_dmx_init failed: error %d\n", __func__, - result); + err = DVB_REGISTER_ADAPTER(&firesat->adapter, + firedtv_model_names[firesat->type], + THIS_MODULE, dev, adapter_nr); + if (err) + goto fail_log; - dvb_unregister_adapter(firesat->adapter); + /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ + firesat->demux.dmx.capabilities = 0; - return result; - } - - firesat->dmxdev.filternum = 16; - firesat->dmxdev.demux = &firesat->demux.dmx; - firesat->dmxdev.capabilities = 0; - - if ((result = dvb_dmxdev_init(&firesat->dmxdev, firesat->adapter)) < 0) { - printk("%s: dvb_dmxdev_init failed: error %d\n", - __func__, result); + firesat->demux.priv = (void *)firesat; + firesat->demux.filternum = 16; + firesat->demux.feednum = 16; + firesat->demux.start_feed = firesat_start_feed; + firesat->demux.stop_feed = firesat_stop_feed; + firesat->demux.write_to_decoder = NULL; - dvb_dmx_release(&firesat->demux); - dvb_unregister_adapter(firesat->adapter); + err = dvb_dmx_init(&firesat->demux); + if (err) + goto fail_unreg_adapter; - return result; - } + firesat->dmxdev.filternum = 16; + firesat->dmxdev.demux = &firesat->demux.dmx; + firesat->dmxdev.capabilities = 0; - firesat->frontend.source = DMX_FRONTEND_0; + err = dvb_dmxdev_init(&firesat->dmxdev, &firesat->adapter); + if (err) + goto fail_dmx_release; - if ((result = firesat->demux.dmx.add_frontend(&firesat->demux.dmx, - &firesat->frontend)) < 0) { - printk("%s: dvb_dmx_init failed: error %d\n", __func__, - result); + firesat->frontend.source = DMX_FRONTEND_0; - dvb_dmxdev_release(&firesat->dmxdev); - dvb_dmx_release(&firesat->demux); - dvb_unregister_adapter(firesat->adapter); + err = firesat->demux.dmx.add_frontend(&firesat->demux.dmx, + &firesat->frontend); + if (err) + goto fail_dmxdev_release; - return result; - } + err = firesat->demux.dmx.connect_frontend(&firesat->demux.dmx, + &firesat->frontend); + if (err) + goto fail_rem_frontend; - if ((result = firesat->demux.dmx.connect_frontend(&firesat->demux.dmx, - &firesat->frontend)) < 0) { - printk("%s: dvb_dmx_init failed: error %d\n", __func__, - result); + dvb_net_init(&firesat->adapter, &firesat->dvbnet, &firesat->demux.dmx); - firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, &firesat->frontend); - dvb_dmxdev_release(&firesat->dmxdev); - dvb_dmx_release(&firesat->demux); - dvb_unregister_adapter(firesat->adapter); + firesat_frontend_init(firesat); + err = dvb_register_frontend(&firesat->adapter, &firesat->fe); + if (err) + goto fail_net_release; - return result; - } + err = firesat_ca_register(firesat); + if (err) + dev_info(dev, "Conditional Access Module not enabled\n"); - dvb_net_init(firesat->adapter, &firesat->dvbnet, &firesat->demux.dmx); - -// fe->ops = firesat_ops; -// fe->dvb = firesat->adapter; - firesat_frontend_attach(firesat, fe); - - fe->sec_priv = firesat; //IMPORTANT, functions depend on this!!! - if ((result= dvb_register_frontend(firesat->adapter, fe)) < 0) { - printk("%s: dvb_register_frontend_new failed: error %d\n", __func__, result); - /* ### cleanup */ - return result; - } - - firesat_ca_init(firesat); + return 0; - return 0; +fail_net_release: + dvb_net_release(&firesat->dvbnet); + firesat->demux.dmx.close(&firesat->demux.dmx); +fail_rem_frontend: + firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, + &firesat->frontend); +fail_dmxdev_release: + dvb_dmxdev_release(&firesat->dmxdev); +fail_dmx_release: + dvb_dmx_release(&firesat->demux); +fail_unreg_adapter: + dvb_unregister_adapter(&firesat->adapter); +fail_log: + dev_err(dev, "DVB initialization failed\n"); + return err; } diff --git a/drivers/media/dvb/firesat/firesat_fe.c b/drivers/media/dvb/firesat/firesat_fe.c index ec614ea8de22..1ed972b79573 100644 --- a/drivers/media/dvb/firesat/firesat_fe.c +++ b/drivers/media/dvb/firesat/firesat_fe.c @@ -12,6 +12,7 @@ #include <linux/errno.h> #include <linux/kernel.h> +#include <linux/string.h> #include <linux/types.h> #include <dvb_frontend.h> @@ -22,22 +23,21 @@ static int firesat_dvb_init(struct dvb_frontend *fe) { - int result; struct firesat *firesat = fe->sec_priv; -// printk("fdi: 1\n"); - firesat->isochannel = firesat->adapter->num; //<< 1 | (firesat->subunit & 0x1); // ### ask IRM -// printk("fdi: 2\n"); - result = try_CMPEstablishPPconnection(firesat, firesat->subunit, firesat->isochannel); - if (result != 0) { + int err; + + /* FIXME - allocate free channel at IRM */ + firesat->isochannel = firesat->adapter.num; + + err = cmp_establish_pp_connection(firesat, firesat->subunit, + firesat->isochannel); + if (err) { printk(KERN_ERR "Could not establish point to point " "connection.\n"); - return -1; + return err; } -// printk("fdi: 3\n"); - result = setup_iso_channel(firesat); -// printk("fdi: 4. Result was %d\n", result); - return result; + return setup_iso_channel(firesat); } static int firesat_sleep(struct dvb_frontend *fe) @@ -45,7 +45,7 @@ static int firesat_sleep(struct dvb_frontend *fe) struct firesat *firesat = fe->sec_priv; tear_down_iso_channel(firesat); - try_CMPBreakPPconnection(firesat, firesat->subunit, firesat->isochannel); + cmp_break_pp_connection(firesat, firesat->subunit, firesat->isochannel); firesat->isochannel = -1; return 0; } @@ -55,8 +55,8 @@ static int firesat_diseqc_send_master_cmd(struct dvb_frontend *fe, { struct firesat *firesat = fe->sec_priv; - return AVCLNBControl(firesat, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, - LNBCONTROL_DONTCARE, 1, cmd); + return avc_lnb_control(firesat, LNBCONTROL_DONTCARE, + LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, 1, cmd); } static int firesat_diseqc_send_burst(struct dvb_frontend *fe, @@ -82,24 +82,19 @@ static int firesat_set_voltage(struct dvb_frontend *fe, return 0; } -static int firesat_read_status (struct dvb_frontend *fe, fe_status_t *status) +static int firesat_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct firesat *firesat = fe->sec_priv; ANTENNA_INPUT_INFO info; - if (AVCTunerStatus(firesat, &info)) + if (avc_tuner_status(firesat, &info)) return -EINVAL; - if (info.NoRF) { + if (info.NoRF) *status = 0; - } else { - *status = FE_HAS_SIGNAL | - FE_HAS_VITERBI | - FE_HAS_SYNC | - FE_HAS_CARRIER | - FE_HAS_LOCK; - } - + else + *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_CARRIER | FE_HAS_LOCK; return 0; } @@ -108,14 +103,11 @@ static int firesat_read_ber(struct dvb_frontend *fe, u32 *ber) struct firesat *firesat = fe->sec_priv; ANTENNA_INPUT_INFO info; - if (AVCTunerStatus(firesat, &info)) + if (avc_tuner_status(firesat, &info)) return -EINVAL; - *ber = (info.BER[0] << 24) | - (info.BER[1] << 16) | - (info.BER[2] << 8) | - info.BER[3]; - + *ber = info.BER[0] << 24 | info.BER[1] << 16 | + info.BER[2] << 8 | info.BER[3]; return 0; } @@ -124,11 +116,10 @@ static int firesat_read_signal_strength (struct dvb_frontend *fe, u16 *strength) struct firesat *firesat = fe->sec_priv; ANTENNA_INPUT_INFO info; - if (AVCTunerStatus(firesat, &info)) + if (avc_tuner_status(firesat, &info)) return -EINVAL; *strength = info.SignalStrength << 8; - return 0; } @@ -137,14 +128,12 @@ static int firesat_read_snr(struct dvb_frontend *fe, u16 *snr) struct firesat *firesat = fe->sec_priv; ANTENNA_INPUT_INFO info; - if (AVCTunerStatus(firesat, &info)) + if (avc_tuner_status(firesat, &info)) return -EINVAL; - *snr = (info.CarrierNoiseRatio[0] << 8) + - info.CarrierNoiseRatio[1]; + /* C/N[dB] = -10 * log10(snr / 65535) */ + *snr = (info.CarrierNoiseRatio[0] << 8) + info.CarrierNoiseRatio[1]; *snr *= 257; - // C/N[dB] = -10 * log10(snr / 65535) - return 0; } @@ -158,10 +147,11 @@ static int firesat_set_frontend(struct dvb_frontend *fe, { struct firesat *firesat = fe->sec_priv; - if (AVCTuner_DSD(firesat, params, NULL) != ACCEPTED) + /* FIXME: avc_tuner_dsd never returns ACCEPTED. Check status? */ + if (avc_tuner_dsd(firesat, params) != ACCEPTED) return -EINVAL; else - return 0; //not sure of this... + return 0; /* not sure of this... */ } static int firesat_get_frontend(struct dvb_frontend *fe, @@ -170,107 +160,86 @@ static int firesat_get_frontend(struct dvb_frontend *fe, return -EOPNOTSUPP; } -static struct dvb_frontend_info firesat_S_frontend_info; -static struct dvb_frontend_info firesat_C_frontend_info; -static struct dvb_frontend_info firesat_T_frontend_info; - -static struct dvb_frontend_ops firesat_ops = { +void firesat_frontend_init(struct firesat *firesat) +{ + struct dvb_frontend_ops *ops = &firesat->fe.ops; + struct dvb_frontend_info *fi = &ops->info; - .init = firesat_dvb_init, - .sleep = firesat_sleep, + ops->init = firesat_dvb_init; + ops->sleep = firesat_sleep; - .set_frontend = firesat_set_frontend, - .get_frontend = firesat_get_frontend, + ops->set_frontend = firesat_set_frontend; + ops->get_frontend = firesat_get_frontend; - .read_status = firesat_read_status, - .read_ber = firesat_read_ber, - .read_signal_strength = firesat_read_signal_strength, - .read_snr = firesat_read_snr, - .read_ucblocks = firesat_read_uncorrected_blocks, + ops->read_status = firesat_read_status; + ops->read_ber = firesat_read_ber; + ops->read_signal_strength = firesat_read_signal_strength; + ops->read_snr = firesat_read_snr; + ops->read_ucblocks = firesat_read_uncorrected_blocks; - .diseqc_send_master_cmd = firesat_diseqc_send_master_cmd, - .diseqc_send_burst = firesat_diseqc_send_burst, - .set_tone = firesat_set_tone, - .set_voltage = firesat_set_voltage, -}; + ops->diseqc_send_master_cmd = firesat_diseqc_send_master_cmd; + ops->diseqc_send_burst = firesat_diseqc_send_burst; + ops->set_tone = firesat_set_tone; + ops->set_voltage = firesat_set_voltage; -int firesat_frontend_attach(struct firesat *firesat, struct dvb_frontend *fe) -{ switch (firesat->type) { case FireSAT_DVB_S: - firesat->frontend_info = &firesat_S_frontend_info; + fi->type = FE_QPSK; + + fi->frequency_min = 950000; + fi->frequency_max = 2150000; + fi->frequency_stepsize = 125; + fi->symbol_rate_min = 1000000; + fi->symbol_rate_max = 40000000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK; break; + case FireSAT_DVB_C: - firesat->frontend_info = &firesat_C_frontend_info; + fi->type = FE_QAM; + + fi->frequency_min = 47000000; + fi->frequency_max = 866000000; + fi->frequency_stepsize = 62500; + fi->symbol_rate_min = 870000; + fi->symbol_rate_max = 6900000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO; break; + case FireSAT_DVB_T: - firesat->frontend_info = &firesat_T_frontend_info; + fi->type = FE_OFDM; + + fi->frequency_min = 49000000; + fi->frequency_max = 861000000; + fi->frequency_stepsize = 62500; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_2_3 | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO; break; + default: - printk(KERN_ERR "firedtv: no frontend for model type 0x%x\n", + printk(KERN_ERR "FireDTV: no frontend for model type %d\n", firesat->type); - firesat->frontend_info = NULL; } - fe->ops = firesat_ops; - fe->ops.info = *(firesat->frontend_info); - fe->dvb = firesat->adapter; + strcpy(fi->name, firedtv_model_names[firesat->type]); - return 0; + firesat->fe.dvb = &firesat->adapter; + firesat->fe.sec_priv = firesat; } - -static struct dvb_frontend_info firesat_S_frontend_info = { - - .name = "FireDTV DVB-S Frontend", - .type = FE_QPSK, - - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, - .symbol_rate_min = 1000000, - .symbol_rate_max = 40000000, - - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK, -}; - -static struct dvb_frontend_info firesat_C_frontend_info = { - - .name = "FireDTV DVB-C Frontend", - .type = FE_QAM, - - .frequency_min = 47000000, - .frequency_max = 866000000, - .frequency_stepsize = 62500, - .symbol_rate_min = 870000, - .symbol_rate_max = 6900000, - - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_QAM_AUTO, -}; - -static struct dvb_frontend_info firesat_T_frontend_info = { - - .name = "FireDTV DVB-T Frontend", - .type = FE_OFDM, - - .frequency_min = 49000000, - .frequency_max = 861000000, - .frequency_stepsize = 62500, - - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_2_3 | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO, -}; diff --git a/drivers/media/dvb/firesat/firesat_iso.c b/drivers/media/dvb/firesat/firesat_iso.c index bc94afe57f63..b3c61f95fa94 100644 --- a/drivers/media/dvb/firesat/firesat_iso.c +++ b/drivers/media/dvb/firesat/firesat_iso.c @@ -18,6 +18,7 @@ #include <dma.h> #include <iso.h> +#include <nodemgr.h> #include "firesat.h" @@ -36,7 +37,7 @@ int setup_iso_channel(struct firesat *firesat) { int result; firesat->iso_handle = - hpsb_iso_recv_init(firesat->host, + hpsb_iso_recv_init(firesat->ud->ne->host, 256 * 200, //data_buf_size, 256, //buf_packets, firesat->isochannel, @@ -59,7 +60,6 @@ static void rawiso_activity_cb(struct hpsb_iso *iso) { unsigned int num; unsigned int i; -/* unsigned int j; */ unsigned int packet; unsigned long flags; struct firesat *firesat = NULL; @@ -88,12 +88,7 @@ static void rawiso_activity_cb(struct hpsb_iso *iso) (188 + sizeof(struct firewireheader)); if (iso->infos[packet].len <= sizeof(struct CIPHeader)) continue; // ignore empty packet -/* printk("%s: Handling packets (%d): ", __func__, */ -/* iso->infos[packet].len); */ -/* for (j = 0; j < iso->infos[packet].len - */ -/* sizeof(struct CIPHeader); j++) */ -/* printk("%02X,", buf[j]); */ -/* printk("\n"); */ + while (count --) { if (buf[sizeof(struct firewireheader)] == 0x47) dvb_dmx_swfilter_packets(&firesat->demux, -- cgit v1.2.3 From 096edfbf167ab277608d26ba8b7978da116a4996 Mon Sep 17 00:00:00 2001 From: Henrik Kurelid <henrik@kurelid.se> Date: Thu, 4 Dec 2008 22:40:52 +0100 Subject: firedtv: fix returned struct for ca_info The SystemId of the ca_info message was filled with garbage. It now returns what the card returns. Signed-off-by: Henrik Kurelid <henrik@kurelid.se> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/avc_api.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c index 56911f3df7f6..3a4da73f0798 100644 --- a/drivers/media/dvb/firesat/avc_api.c +++ b/drivers/media/dvb/firesat/avc_api.c @@ -763,7 +763,7 @@ int avc_ca_info(struct firesat *firesat, char *app_info, unsigned int *len) { AVCCmdFrm CmdFrm; AVCRspFrm RspFrm; - /* int pos; FIXME: unused */ + int pos; memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); CmdFrm.cts = AVC; @@ -783,13 +783,13 @@ int avc_ca_info(struct firesat *firesat, char *app_info, unsigned int *len) if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) return -EIO; - /* pos = get_ca_object_pos(&RspFrm); FIXME: unused */ + pos = get_ca_object_pos(&RspFrm); app_info[0] = (TAG_CA_INFO >> 16) & 0xFF; app_info[1] = (TAG_CA_INFO >> 8) & 0xFF; app_info[2] = (TAG_CA_INFO >> 0) & 0xFF; app_info[3] = 2; - app_info[4] = app_info[5]; - app_info[5] = app_info[6]; + app_info[4] = RspFrm.operand[pos + 0]; + app_info[5] = RspFrm.operand[pos + 1]; *len = app_info[3] + 4; return 0; -- cgit v1.2.3 From 7199e523ef71d24cd8030ea454fca00bb52d58f0 Mon Sep 17 00:00:00 2001 From: Henrik Kurelid <henrik@kurelid.se> Date: Fri, 5 Dec 2008 10:00:16 +0100 Subject: firedtv: use length_field() of PMT as length Parsed and used the length_field() of the PMT message instead of using the length field of the message struct, which does not seem to be filled correctly by e.g. MythTV. Signed-off-by: Henrik Kurelid <henrik@kurelid.se> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/firesat-ci.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/firesat-ci.c b/drivers/media/dvb/firesat/firesat-ci.c index 0deb47eefa10..783ed2000102 100644 --- a/drivers/media/dvb/firesat/firesat-ci.c +++ b/drivers/media/dvb/firesat/firesat-ci.c @@ -127,14 +127,20 @@ static int firesat_ca_pmt(struct firesat *firesat, void *arg) { struct ca_msg *msg = arg; int data_pos; + int data_length; + int i; + + data_pos = 4; + if (msg->msg[3] & 0x80) { + data_length = 0; + for (i = 0; i < (msg->msg[3] & 0x7F); i++) + data_length = (data_length << 8) + msg->msg[data_pos++]; + } else { + data_length = msg->msg[3]; + } - if (msg->msg[3] & 0x80) - data_pos = (msg->msg[4] && 0x7F) + 4; - else - data_pos = 4; - - return avc_ca_pmt(firesat, &msg->msg[data_pos], - msg->length - data_pos) ? -EFAULT : 0; + return avc_ca_pmt(firesat, &msg->msg[data_pos], data_length) ? + -EFAULT : 0; } static int firesat_ca_send_msg(struct firesat *firesat, void *arg) -- cgit v1.2.3 From a40bf5591681f707afcf550cdcb4cc1697a29504 Mon Sep 17 00:00:00 2001 From: Henrik Kurelid <henrik@kurelid.se> Date: Mon, 15 Dec 2008 08:17:12 +0100 Subject: firedtv: fix registration - adapter number could only be zero There was a bug causing the initialization to fail if adapter number was greater than zero. The adapter was however registered which caused the driver to oops the second time initialization was tried. Signed-off-by: Henrik Kurelid <henrik@kurelid.se> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/firesat_dvb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/firesat_dvb.c b/drivers/media/dvb/firesat/firesat_dvb.c index cfa3a2e8edd1..cb36c0310199 100644 --- a/drivers/media/dvb/firesat/firesat_dvb.c +++ b/drivers/media/dvb/firesat/firesat_dvb.c @@ -206,7 +206,7 @@ int firesat_dvbdev_init(struct firesat *firesat, struct device *dev) err = DVB_REGISTER_ADAPTER(&firesat->adapter, firedtv_model_names[firesat->type], THIS_MODULE, dev, adapter_nr); - if (err) + if (err < 0) goto fail_log; /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ -- cgit v1.2.3 From 291f006efeebeeb2073289e44efb8f97cf157220 Mon Sep 17 00:00:00 2001 From: Julia Lawall <julia@diku.dk> Date: Thu, 25 Dec 2008 15:34:25 +0100 Subject: firedtv: Use DEFINE_SPINLOCK SPIN_LOCK_UNLOCKED is deprecated. The following makes the change suggested in Documentation/spinlocks.txt Signed-off-by: Julia Lawall <julia@diku.dk> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firesat/firesat_1394.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c index 82bd9786571d..11db62730256 100644 --- a/drivers/media/dvb/firesat/firesat_1394.c +++ b/drivers/media/dvb/firesat/firesat_1394.c @@ -94,7 +94,7 @@ MODULE_DEVICE_TABLE(ieee1394, firesat_id_table); /* list of all firesat devices */ LIST_HEAD(firesat_list); -spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(firesat_list_lock); static void fcp_request(struct hpsb_host *host, int nodeid, -- cgit v1.2.3 From a70f81c1c0dac113ac4705e7701e2676e67905cd Mon Sep 17 00:00:00 2001 From: Rambaldi <Rambaldi@xs4all.nl> Date: Sat, 17 Jan 2009 14:47:34 +0100 Subject: firedtv: rename files, variables, functions from firesat to firedtv Combination of the following changes: Sat, 17 Jan 2009 14:47:34 +0100 firedtv: rename variables and functions from firesat to firedtv Signed-off-by: Rambaldi <Rambaldi@xs4all.nl> Additional changes by Stefan Richter: Renamed struct firedtv *firedtv to struct firedtv *fdtv and firedtv_foo_bar() to fdtv_foo_bar() for brevity. Sat, 17 Jan 2009 13:07:44 +0100 firedtv: rename files from firesat to firedtv Signed-off-by: Rambaldi <Rambaldi@xs4all.nl> Additional changes by Stefan Richter: Name the directory "firewire" instead of "firedtv". Standardize on "-" instead of "_" in file names, because that's what drivers/firewire/ and drivers/media/dvb/dvb-usb/ use too. Build fix. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/Kconfig | 2 +- drivers/media/dvb/Makefile | 2 +- drivers/media/dvb/firesat/Kconfig | 12 - drivers/media/dvb/firesat/Makefile | 13 - drivers/media/dvb/firesat/avc_api.c | 1051 ----------------------------- drivers/media/dvb/firesat/avc_api.h | 432 ------------ drivers/media/dvb/firesat/cmp.c | 171 ----- drivers/media/dvb/firesat/cmp.h | 9 - drivers/media/dvb/firesat/firesat-ci.c | 261 ------- drivers/media/dvb/firesat/firesat-ci.h | 9 - drivers/media/dvb/firesat/firesat-rc.c | 191 ------ drivers/media/dvb/firesat/firesat-rc.h | 11 - drivers/media/dvb/firesat/firesat.h | 227 ------- drivers/media/dvb/firesat/firesat_1394.c | 291 -------- drivers/media/dvb/firesat/firesat_dvb.c | 276 -------- drivers/media/dvb/firesat/firesat_fe.c | 245 ------- drivers/media/dvb/firesat/firesat_iso.c | 111 --- drivers/media/dvb/firewire/Kconfig | 12 + drivers/media/dvb/firewire/Makefile | 13 + drivers/media/dvb/firewire/avc.c | 1051 +++++++++++++++++++++++++++++ drivers/media/dvb/firewire/avc.h | 432 ++++++++++++ drivers/media/dvb/firewire/cmp.c | 171 +++++ drivers/media/dvb/firewire/cmp.h | 9 + drivers/media/dvb/firewire/firedtv-1394.c | 291 ++++++++ drivers/media/dvb/firewire/firedtv-ci.c | 261 +++++++ drivers/media/dvb/firewire/firedtv-ci.h | 9 + drivers/media/dvb/firewire/firedtv-dvb.c | 276 ++++++++ drivers/media/dvb/firewire/firedtv-fe.c | 245 +++++++ drivers/media/dvb/firewire/firedtv-iso.c | 111 +++ drivers/media/dvb/firewire/firedtv-rc.c | 191 ++++++ drivers/media/dvb/firewire/firedtv-rc.h | 11 + drivers/media/dvb/firewire/firedtv.h | 227 +++++++ 32 files changed, 3312 insertions(+), 3312 deletions(-) delete mode 100644 drivers/media/dvb/firesat/Kconfig delete mode 100644 drivers/media/dvb/firesat/Makefile delete mode 100644 drivers/media/dvb/firesat/avc_api.c delete mode 100644 drivers/media/dvb/firesat/avc_api.h delete mode 100644 drivers/media/dvb/firesat/cmp.c delete mode 100644 drivers/media/dvb/firesat/cmp.h delete mode 100644 drivers/media/dvb/firesat/firesat-ci.c delete mode 100644 drivers/media/dvb/firesat/firesat-ci.h delete mode 100644 drivers/media/dvb/firesat/firesat-rc.c delete mode 100644 drivers/media/dvb/firesat/firesat-rc.h delete mode 100644 drivers/media/dvb/firesat/firesat.h delete mode 100644 drivers/media/dvb/firesat/firesat_1394.c delete mode 100644 drivers/media/dvb/firesat/firesat_dvb.c delete mode 100644 drivers/media/dvb/firesat/firesat_fe.c delete mode 100644 drivers/media/dvb/firesat/firesat_iso.c create mode 100644 drivers/media/dvb/firewire/Kconfig create mode 100644 drivers/media/dvb/firewire/Makefile create mode 100644 drivers/media/dvb/firewire/avc.c create mode 100644 drivers/media/dvb/firewire/avc.h create mode 100644 drivers/media/dvb/firewire/cmp.c create mode 100644 drivers/media/dvb/firewire/cmp.h create mode 100644 drivers/media/dvb/firewire/firedtv-1394.c create mode 100644 drivers/media/dvb/firewire/firedtv-ci.c create mode 100644 drivers/media/dvb/firewire/firedtv-ci.h create mode 100644 drivers/media/dvb/firewire/firedtv-dvb.c create mode 100644 drivers/media/dvb/firewire/firedtv-fe.c create mode 100644 drivers/media/dvb/firewire/firedtv-iso.c create mode 100644 drivers/media/dvb/firewire/firedtv-rc.c create mode 100644 drivers/media/dvb/firewire/firedtv-rc.h create mode 100644 drivers/media/dvb/firewire/firedtv.h (limited to 'drivers') diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 8a2d5f9713de..5a74c5c62f15 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -51,7 +51,7 @@ comment "Supported SDMC DM1105 Adapters" depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/dm1105/Kconfig" -source "drivers/media/dvb/firesat/Kconfig" +source "drivers/media/dvb/firewire/Kconfig" comment "Supported DVB Frontends" depends on DVB_CORE diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index cb765816f76c..6092a5bb5a7d 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -4,4 +4,4 @@ obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ -obj-$(CONFIG_DVB_FIREDTV) += firesat/ +obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/drivers/media/dvb/firesat/Kconfig b/drivers/media/dvb/firesat/Kconfig deleted file mode 100644 index 03d25ad10350..000000000000 --- a/drivers/media/dvb/firesat/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config DVB_FIREDTV - tristate "FireDTV (FireWire attached DVB receivers)" - depends on DVB_CORE && IEEE1394 && INPUT - help - Support for DVB receivers from Digital Everywhere, known as FireDTV - and FloppyDTV, which are connected via IEEE 1394 (FireWire). - - These devices don't have an MPEG decoder built in, so you need - an external software decoder to watch TV. - - To compile this driver as a module, say M here: the module will be - called firedtv. diff --git a/drivers/media/dvb/firesat/Makefile b/drivers/media/dvb/firesat/Makefile deleted file mode 100644 index 9e49edc7c49d..000000000000 --- a/drivers/media/dvb/firesat/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -firedtv-objs := firesat_1394.o \ - firesat_dvb.o \ - firesat_fe.o \ - firesat_iso.o \ - avc_api.o \ - cmp.o \ - firesat-rc.o \ - firesat-ci.o - -obj-$(CONFIG_DVB_FIREDTV) += firedtv.o - -EXTRA_CFLAGS := -Idrivers/ieee1394 -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/dvb/firesat/avc_api.c b/drivers/media/dvb/firesat/avc_api.c deleted file mode 100644 index 3a4da73f0798..000000000000 --- a/drivers/media/dvb/firesat/avc_api.c +++ /dev/null @@ -1,1051 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Ben Backx <ben@bbackx.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/bug.h> -#include <linux/crc32.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/moduleparam.h> -#include <linux/mutex.h> -#include <linux/string.h> -#include <linux/wait.h> -#include <linux/workqueue.h> - -#include <ieee1394_transactions.h> -#include <nodemgr.h> - -#include "avc_api.h" -#include "firesat.h" -#include "firesat-rc.h" - -#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL - -static int __avc_write(struct firesat *firesat, - const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) -{ - int err, retry; - - if (RspFrm) - firesat->avc_reply_received = false; - - for (retry = 0; retry < 6; retry++) { - err = hpsb_node_write(firesat->ud->ne, FCP_COMMAND_REGISTER, - (quadlet_t *)CmdFrm, CmdFrm->length); - if (err) { - firesat->avc_reply_received = true; - dev_err(&firesat->ud->device, - "FCP command write failed\n"); - return err; - } - - if (!RspFrm) - return 0; - - /* - * AV/C specs say that answers should be sent within 150 ms. - * Time out after 200 ms. - */ - if (wait_event_timeout(firesat->avc_wait, - firesat->avc_reply_received, - HZ / 5) != 0) { - memcpy(RspFrm, firesat->respfrm, firesat->resp_length); - RspFrm->length = firesat->resp_length; - - return 0; - } - } - dev_err(&firesat->ud->device, "FCP response timed out\n"); - return -ETIMEDOUT; -} - -static int avc_write(struct firesat *firesat, - const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) -{ - int ret; - - if (mutex_lock_interruptible(&firesat->avc_mutex)) - return -EINTR; - - ret = __avc_write(firesat, CmdFrm, RspFrm); - - mutex_unlock(&firesat->avc_mutex); - return ret; -} - -int avc_recv(struct firesat *firesat, u8 *data, size_t length) -{ - AVCRspFrm *RspFrm = (AVCRspFrm *)data; - - if (length >= 8 && - RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && - RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && - RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && - RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { - if (RspFrm->resp == CHANGED) { - firesat_handle_rc(firesat, - RspFrm->operand[4] << 8 | RspFrm->operand[5]); - schedule_work(&firesat->remote_ctrl_work); - } else if (RspFrm->resp != INTERIM) { - dev_info(&firesat->ud->device, - "remote control result = %d\n", RspFrm->resp); - } - return 0; - } - - if (firesat->avc_reply_received) { - dev_err(&firesat->ud->device, - "received out-of-order AVC response, ignored\n"); - return -EIO; - } - - memcpy(firesat->respfrm, data, length); - firesat->resp_length = length; - - firesat->avc_reply_received = true; - wake_up(&firesat->avc_wait); - - return 0; -} - -/* - * tuning command for setting the relative LNB frequency - * (not supported by the AVC standard) - */ -static void avc_tuner_tuneqpsk(struct firesat *firesat, - struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) -{ - CmdFrm->opcode = VENDOR; - - CmdFrm->operand[0] = SFE_VENDOR_DE_COMPANYID_0; - CmdFrm->operand[1] = SFE_VENDOR_DE_COMPANYID_1; - CmdFrm->operand[2] = SFE_VENDOR_DE_COMPANYID_2; - CmdFrm->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; - - CmdFrm->operand[4] = (params->frequency >> 24) & 0xff; - CmdFrm->operand[5] = (params->frequency >> 16) & 0xff; - CmdFrm->operand[6] = (params->frequency >> 8) & 0xff; - CmdFrm->operand[7] = params->frequency & 0xff; - - CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; - CmdFrm->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; - - switch(params->u.qpsk.fec_inner) { - case FEC_1_2: - CmdFrm->operand[10] = 0x1; break; - case FEC_2_3: - CmdFrm->operand[10] = 0x2; break; - case FEC_3_4: - CmdFrm->operand[10] = 0x3; break; - case FEC_5_6: - CmdFrm->operand[10] = 0x4; break; - case FEC_7_8: - CmdFrm->operand[10] = 0x5; break; - case FEC_4_5: - case FEC_8_9: - case FEC_AUTO: - default: - CmdFrm->operand[10] = 0x0; - } - - if (firesat->voltage == 0xff) - CmdFrm->operand[11] = 0xff; - else if (firesat->voltage == SEC_VOLTAGE_18) /* polarisation */ - CmdFrm->operand[11] = 0; - else - CmdFrm->operand[11] = 1; - - if (firesat->tone == 0xff) - CmdFrm->operand[12] = 0xff; - else if (firesat->tone == SEC_TONE_ON) /* band */ - CmdFrm->operand[12] = 1; - else - CmdFrm->operand[12] = 0; - - if (firesat->type == FireSAT_DVB_S2) { - CmdFrm->operand[13] = 0x1; - CmdFrm->operand[14] = 0xff; - CmdFrm->operand[15] = 0xff; - CmdFrm->length = 20; - } else { - CmdFrm->length = 16; - } -} - -static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, - AVCCmdFrm *CmdFrm) -{ - M_VALID_FLAGS flags; - - flags.Bits.Modulation = params->u.qam.modulation != QAM_AUTO; - flags.Bits.FEC_inner = params->u.qam.fec_inner != FEC_AUTO; - flags.Bits.FEC_outer = 0; - flags.Bits.Symbol_Rate = 1; - flags.Bits.Frequency = 1; - flags.Bits.Orbital_Pos = 0; - flags.Bits.Polarisation = 0; - flags.Bits.reserved_fields = 0; - flags.Bits.reserved1 = 0; - flags.Bits.Network_ID = 0; - - CmdFrm->opcode = DSD; - - CmdFrm->operand[0] = 0; /* source plug */ - CmdFrm->operand[1] = 0xd2; /* subfunction replace */ - CmdFrm->operand[2] = 0x20; /* system id = DVB */ - CmdFrm->operand[3] = 0x00; /* antenna number */ - /* system_specific_multiplex selection_length */ - CmdFrm->operand[4] = 0x11; - CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ - CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ - CmdFrm->operand[7] = 0x00; - CmdFrm->operand[8] = 0x00; - CmdFrm->operand[9] = 0x00; - CmdFrm->operand[10] = 0x00; - - CmdFrm->operand[11] = - (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); - CmdFrm->operand[12] = - ((params->frequency / 4000) >> 8) & 0xff; - CmdFrm->operand[13] = (params->frequency / 4000) & 0xff; - CmdFrm->operand[14] = - ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; - CmdFrm->operand[15] = - ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; - CmdFrm->operand[16] = - ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; - CmdFrm->operand[17] = 0x00; - - switch (params->u.qpsk.fec_inner) { - case FEC_1_2: - CmdFrm->operand[18] = 0x1; break; - case FEC_2_3: - CmdFrm->operand[18] = 0x2; break; - case FEC_3_4: - CmdFrm->operand[18] = 0x3; break; - case FEC_5_6: - CmdFrm->operand[18] = 0x4; break; - case FEC_7_8: - CmdFrm->operand[18] = 0x5; break; - case FEC_8_9: - CmdFrm->operand[18] = 0x6; break; - case FEC_4_5: - CmdFrm->operand[18] = 0x8; break; - case FEC_AUTO: - default: - CmdFrm->operand[18] = 0x0; - } - switch (params->u.qam.modulation) { - case QAM_16: - CmdFrm->operand[19] = 0x08; break; - case QAM_32: - CmdFrm->operand[19] = 0x10; break; - case QAM_64: - CmdFrm->operand[19] = 0x18; break; - case QAM_128: - CmdFrm->operand[19] = 0x20; break; - case QAM_256: - CmdFrm->operand[19] = 0x28; break; - case QAM_AUTO: - default: - CmdFrm->operand[19] = 0x00; - } - CmdFrm->operand[20] = 0x00; - CmdFrm->operand[21] = 0x00; - /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ - CmdFrm->operand[22] = 0x00; - - CmdFrm->length = 28; -} - -static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, - AVCCmdFrm *CmdFrm) -{ - M_VALID_FLAGS flags; - - flags.Bits_T.GuardInterval = - params->u.ofdm.guard_interval != GUARD_INTERVAL_AUTO; - flags.Bits_T.CodeRateLPStream = - params->u.ofdm.code_rate_LP != FEC_AUTO; - flags.Bits_T.CodeRateHPStream = - params->u.ofdm.code_rate_HP != FEC_AUTO; - flags.Bits_T.HierarchyInfo = - params->u.ofdm.hierarchy_information != HIERARCHY_AUTO; - flags.Bits_T.Constellation = - params->u.ofdm.constellation != QAM_AUTO; - flags.Bits_T.Bandwidth = - params->u.ofdm.bandwidth != BANDWIDTH_AUTO; - flags.Bits_T.CenterFrequency = 1; - flags.Bits_T.reserved1 = 0; - flags.Bits_T.reserved2 = 0; - flags.Bits_T.OtherFrequencyFlag = 0; - flags.Bits_T.TransmissionMode = - params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO; - flags.Bits_T.NetworkId = 0; - - CmdFrm->opcode = DSD; - - CmdFrm->operand[0] = 0; /* source plug */ - CmdFrm->operand[1] = 0xd2; /* subfunction replace */ - CmdFrm->operand[2] = 0x20; /* system id = DVB */ - CmdFrm->operand[3] = 0x00; /* antenna number */ - /* system_specific_multiplex selection_length */ - CmdFrm->operand[4] = 0x0c; - CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ - CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ - CmdFrm->operand[7] = 0x0; - CmdFrm->operand[8] = (params->frequency / 10) >> 24; - CmdFrm->operand[9] = ((params->frequency / 10) >> 16) & 0xff; - CmdFrm->operand[10] = ((params->frequency / 10) >> 8) & 0xff; - CmdFrm->operand[11] = (params->frequency / 10) & 0xff; - - switch (params->u.ofdm.bandwidth) { - case BANDWIDTH_7_MHZ: - CmdFrm->operand[12] = 0x20; break; - case BANDWIDTH_8_MHZ: - case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ - case BANDWIDTH_AUTO: - default: - CmdFrm->operand[12] = 0x00; - } - switch (params->u.ofdm.constellation) { - case QAM_16: - CmdFrm->operand[13] = 1 << 6; break; - case QAM_64: - CmdFrm->operand[13] = 2 << 6; break; - case QPSK: - default: - CmdFrm->operand[13] = 0x00; - } - switch (params->u.ofdm.hierarchy_information) { - case HIERARCHY_1: - CmdFrm->operand[13] |= 1 << 3; break; - case HIERARCHY_2: - CmdFrm->operand[13] |= 2 << 3; break; - case HIERARCHY_4: - CmdFrm->operand[13] |= 3 << 3; break; - case HIERARCHY_AUTO: - case HIERARCHY_NONE: - default: - break; - } - switch (params->u.ofdm.code_rate_HP) { - case FEC_2_3: - CmdFrm->operand[13] |= 1; break; - case FEC_3_4: - CmdFrm->operand[13] |= 2; break; - case FEC_5_6: - CmdFrm->operand[13] |= 3; break; - case FEC_7_8: - CmdFrm->operand[13] |= 4; break; - case FEC_1_2: - default: - break; - } - switch (params->u.ofdm.code_rate_LP) { - case FEC_2_3: - CmdFrm->operand[14] = 1 << 5; break; - case FEC_3_4: - CmdFrm->operand[14] = 2 << 5; break; - case FEC_5_6: - CmdFrm->operand[14] = 3 << 5; break; - case FEC_7_8: - CmdFrm->operand[14] = 4 << 5; break; - case FEC_1_2: - default: - CmdFrm->operand[14] = 0x00; break; - } - switch (params->u.ofdm.guard_interval) { - case GUARD_INTERVAL_1_16: - CmdFrm->operand[14] |= 1 << 3; break; - case GUARD_INTERVAL_1_8: - CmdFrm->operand[14] |= 2 << 3; break; - case GUARD_INTERVAL_1_4: - CmdFrm->operand[14] |= 3 << 3; break; - case GUARD_INTERVAL_1_32: - case GUARD_INTERVAL_AUTO: - default: - break; - } - switch (params->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_8K: - CmdFrm->operand[14] |= 1 << 1; break; - case TRANSMISSION_MODE_2K: - case TRANSMISSION_MODE_AUTO: - default: - break; - } - - CmdFrm->operand[15] = 0x00; /* network_ID[0] */ - CmdFrm->operand[16] = 0x00; /* network_ID[1] */ - /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ - CmdFrm->operand[17] = 0x00; - - CmdFrm->length = 24; -} - -int avc_tuner_dsd(struct firesat *firesat, - struct dvb_frontend_parameters *params) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - - switch (firesat->type) { - case FireSAT_DVB_S: - case FireSAT_DVB_S2: - avc_tuner_tuneqpsk(firesat, params, &CmdFrm); break; - case FireSAT_DVB_C: - avc_tuner_dsd_dvb_c(params, &CmdFrm); break; - case FireSAT_DVB_T: - avc_tuner_dsd_dvb_t(params, &CmdFrm); break; - default: - BUG(); - } - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - msleep(500); -#if 0 - /* FIXME: */ - /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ - if(status) - *status=RspFrm.operand[2]; -#endif - return 0; -} - -int avc_tuner_set_pids(struct firesat *firesat, unsigned char pidc, u16 pid[]) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int pos, k; - - if (pidc > 16 && pidc != 0xff) - return -EINVAL; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = DSD; - - CmdFrm.operand[0] = 0; // source plug - CmdFrm.operand[1] = 0xD2; // subfunction replace - CmdFrm.operand[2] = 0x20; // system id = DVB - CmdFrm.operand[3] = 0x00; // antenna number - CmdFrm.operand[4] = 0x00; // system_specific_multiplex selection_length - CmdFrm.operand[5] = pidc; // Nr_of_dsd_sel_specs - - pos = 6; - if (pidc != 0xff) - for (k = 0; k < pidc; k++) { - CmdFrm.operand[pos++] = 0x13; // flowfunction relay - CmdFrm.operand[pos++] = 0x80; // dsd_sel_spec_valid_flags -> PID - CmdFrm.operand[pos++] = (pid[k] >> 8) & 0x1F; - CmdFrm.operand[pos++] = pid[k] & 0xFF; - CmdFrm.operand[pos++] = 0x00; // tableID - CmdFrm.operand[pos++] = 0x00; // filter_length - } - - CmdFrm.length = ALIGN(3 + pos, 4); - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - msleep(50); - return 0; -} - -int avc_tuner_get_ts(struct firesat *firesat) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = DSIT; - - CmdFrm.operand[0] = 0; // source plug - CmdFrm.operand[1] = 0xD2; // subfunction replace - CmdFrm.operand[2] = 0xFF; //status - CmdFrm.operand[3] = 0x20; // system id = DVB - CmdFrm.operand[4] = 0x00; // antenna number - CmdFrm.operand[5] = 0x0; // system_specific_search_flags - CmdFrm.operand[6] = (firesat->type == FireSAT_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length - CmdFrm.operand[7] = 0x00; // valid_flags [0] - CmdFrm.operand[8] = 0x00; // valid_flags [1] - CmdFrm.operand[7 + (firesat->type == FireSAT_DVB_T)?0x0c:0x11] = 0x00; // nr_of_dsit_sel_specs (always 0) - - CmdFrm.length = (firesat->type == FireSAT_DVB_T)?24:28; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - msleep(250); - return 0; -} - -int avc_identify_subunit(struct firesat *firesat) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm,0,sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; // tuner - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = READ_DESCRIPTOR; - - CmdFrm.operand[0]=DESCRIPTOR_SUBUNIT_IDENTIFIER; - CmdFrm.operand[1]=0xff; - CmdFrm.operand[2]=0x00; - CmdFrm.operand[3]=0x00; // length highbyte - CmdFrm.operand[4]=0x08; // length lowbyte - CmdFrm.operand[5]=0x00; // offset highbyte - CmdFrm.operand[6]=0x0d; // offset lowbyte - - CmdFrm.length=12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if ((RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) || - (RspFrm.operand[3] << 8) + RspFrm.operand[4] != 8) { - dev_err(&firesat->ud->device, - "cannot read subunit identifier\n"); - return -EINVAL; - } - return 0; -} - -int avc_tuner_status(struct firesat *firesat, - ANTENNA_INPUT_INFO *antenna_input_info) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int length; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts=AVC; - CmdFrm.ctype=CONTROL; - CmdFrm.sutyp=0x05; // tuner - CmdFrm.suid=firesat->subunit; - CmdFrm.opcode=READ_DESCRIPTOR; - - CmdFrm.operand[0]=DESCRIPTOR_TUNER_STATUS; - CmdFrm.operand[1]=0xff; //read_result_status - CmdFrm.operand[2]=0x00; // reserver - CmdFrm.operand[3]=0;//sizeof(ANTENNA_INPUT_INFO) >> 8; - CmdFrm.operand[4]=0;//sizeof(ANTENNA_INPUT_INFO) & 0xFF; - CmdFrm.operand[5]=0x00; - CmdFrm.operand[6]=0x00; - CmdFrm.length=12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if (RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { - dev_err(&firesat->ud->device, "cannot read tuner status\n"); - return -EINVAL; - } - - length = RspFrm.operand[9]; - if (RspFrm.operand[1] != 0x10 || length != sizeof(ANTENNA_INPUT_INFO)) { - dev_err(&firesat->ud->device, "got invalid tuner status\n"); - return -EINVAL; - } - - memcpy(antenna_input_info, &RspFrm.operand[10], length); - return 0; -} - -int avc_lnb_control(struct firesat *firesat, char voltage, char burst, - char conttone, char nrdiseq, - struct dvb_diseqc_master_cmd *diseqcmd) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int i, j, k; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts=AVC; - CmdFrm.ctype=CONTROL; - CmdFrm.sutyp=0x05; - CmdFrm.suid=firesat->subunit; - CmdFrm.opcode=VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_LNB_CONTROL; - - CmdFrm.operand[4]=voltage; - CmdFrm.operand[5]=nrdiseq; - - i=6; - - for (j = 0; j < nrdiseq; j++) { - CmdFrm.operand[i++] = diseqcmd[j].msg_len; - - for (k = 0; k < diseqcmd[j].msg_len; k++) - CmdFrm.operand[i++] = diseqcmd[j].msg[k]; - } - - CmdFrm.operand[i++]=burst; - CmdFrm.operand[i++]=conttone; - - CmdFrm.length = ALIGN(3 + i, 4); - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if (RspFrm.resp != ACCEPTED) { - dev_err(&firesat->ud->device, "LNB control failed\n"); - return -EINVAL; - } - - return 0; -} - -int avc_register_remote_control(struct firesat *firesat) -{ - AVCCmdFrm CmdFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = NOTIFY; - CmdFrm.sutyp = 0x1f; - CmdFrm.suid = 0x7; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0] = SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1] = SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2] = SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; - - CmdFrm.length = 8; - - return avc_write(firesat, &CmdFrm, NULL); -} - -void avc_remote_ctrl_work(struct work_struct *work) -{ - struct firesat *firesat = - container_of(work, struct firesat, remote_ctrl_work); - - /* Should it be rescheduled in failure cases? */ - avc_register_remote_control(firesat); -} - -#if 0 /* FIXME: unused */ -int avc_tuner_host2ca(struct firesat *firesat) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - return 0; -} -#endif - -static int get_ca_object_pos(AVCRspFrm *RspFrm) -{ - int length = 1; - - /* Check length of length field */ - if (RspFrm->operand[7] & 0x80) - length = (RspFrm->operand[7] & 0x7f) + 1; - return length + 7; -} - -static int get_ca_object_length(AVCRspFrm *RspFrm) -{ -#if 0 /* FIXME: unused */ - int size = 0; - int i; - - if (RspFrm->operand[7] & 0x80) - for (i = 0; i < (RspFrm->operand[7] & 0x7f); i++) { - size <<= 8; - size += RspFrm->operand[8 + i]; - } -#endif - return RspFrm->operand[7]; -} - -int avc_ca_app_info(struct firesat *firesat, char *app_info, unsigned int *len) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int pos; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag - CmdFrm.length = 12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - /* FIXME: check response code and validate response data */ - - pos = get_ca_object_pos(&RspFrm); - app_info[0] = (TAG_APP_INFO >> 16) & 0xFF; - app_info[1] = (TAG_APP_INFO >> 8) & 0xFF; - app_info[2] = (TAG_APP_INFO >> 0) & 0xFF; - app_info[3] = 6 + RspFrm.operand[pos + 4]; - app_info[4] = 0x01; - memcpy(&app_info[5], &RspFrm.operand[pos], 5 + RspFrm.operand[pos + 4]); - *len = app_info[3] + 4; - - return 0; -} - -int avc_ca_info(struct firesat *firesat, char *app_info, unsigned int *len) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int pos; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag - CmdFrm.length = 12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - pos = get_ca_object_pos(&RspFrm); - app_info[0] = (TAG_CA_INFO >> 16) & 0xFF; - app_info[1] = (TAG_CA_INFO >> 8) & 0xFF; - app_info[2] = (TAG_CA_INFO >> 0) & 0xFF; - app_info[3] = 2; - app_info[4] = RspFrm.operand[pos + 0]; - app_info[5] = RspFrm.operand[pos + 1]; - *len = app_info[3] + 4; - - return 0; -} - -int avc_ca_reset(struct firesat *firesat) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_RESET; // ca tag - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 1; // length - CmdFrm.operand[8] = 0; // force hardware reset - CmdFrm.length = 12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - return 0; -} - -int avc_ca_pmt(struct firesat *firesat, char *msg, int length) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int list_management; - int program_info_length; - int pmt_cmd_id; - int read_pos; - int write_pos; - int es_info_length; - int crc32_csum; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - if (msg[0] != LIST_MANAGEMENT_ONLY) { - dev_info(&firesat->ud->device, - "forcing list_management to ONLY\n"); - msg[0] = LIST_MANAGEMENT_ONLY; - } - // We take the cmd_id from the programme level only! - list_management = msg[0]; - program_info_length = ((msg[4] & 0x0F) << 8) + msg[5]; - if (program_info_length > 0) - program_info_length--; // Remove pmt_cmd_id - pmt_cmd_id = msg[6]; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_PMT; // ca tag - CmdFrm.operand[6] = 0; // more/last - //CmdFrm.operand[7] = XXXprogram_info_length + 17; // length - CmdFrm.operand[8] = list_management; - CmdFrm.operand[9] = 0x01; // pmt_cmd=OK_descramble - - // TS program map table - - // Table id=2 - CmdFrm.operand[10] = 0x02; - // Section syntax + length - CmdFrm.operand[11] = 0x80; - //CmdFrm.operand[12] = XXXprogram_info_length + 12; - // Program number - CmdFrm.operand[13] = msg[1]; - CmdFrm.operand[14] = msg[2]; - // Version number=0 + current/next=1 - CmdFrm.operand[15] = 0x01; - // Section number=0 - CmdFrm.operand[16] = 0x00; - // Last section number=0 - CmdFrm.operand[17] = 0x00; - // PCR_PID=1FFF - CmdFrm.operand[18] = 0x1F; - CmdFrm.operand[19] = 0xFF; - // Program info length - CmdFrm.operand[20] = (program_info_length >> 8); - CmdFrm.operand[21] = (program_info_length & 0xFF); - // CA descriptors at programme level - read_pos = 6; - write_pos = 22; - if (program_info_length > 0) { - pmt_cmd_id = msg[read_pos++]; - if (pmt_cmd_id != 1 && pmt_cmd_id != 4) - dev_err(&firesat->ud->device, - "invalid pmt_cmd_id %d\n", pmt_cmd_id); - - memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], - program_info_length); - read_pos += program_info_length; - write_pos += program_info_length; - } - while (read_pos < length) { - CmdFrm.operand[write_pos++] = msg[read_pos++]; - CmdFrm.operand[write_pos++] = msg[read_pos++]; - CmdFrm.operand[write_pos++] = msg[read_pos++]; - es_info_length = - ((msg[read_pos] & 0x0F) << 8) + msg[read_pos + 1]; - read_pos += 2; - if (es_info_length > 0) - es_info_length--; // Remove pmt_cmd_id - CmdFrm.operand[write_pos++] = es_info_length >> 8; - CmdFrm.operand[write_pos++] = es_info_length & 0xFF; - if (es_info_length > 0) { - pmt_cmd_id = msg[read_pos++]; - if (pmt_cmd_id != 1 && pmt_cmd_id != 4) - dev_err(&firesat->ud->device, - "invalid pmt_cmd_id %d " - "at stream level\n", pmt_cmd_id); - - memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], - es_info_length); - read_pos += es_info_length; - write_pos += es_info_length; - } - } - - // CRC - CmdFrm.operand[write_pos++] = 0x00; - CmdFrm.operand[write_pos++] = 0x00; - CmdFrm.operand[write_pos++] = 0x00; - CmdFrm.operand[write_pos++] = 0x00; - - CmdFrm.operand[7] = write_pos - 8; - CmdFrm.operand[12] = write_pos - 13; - - crc32_csum = crc32_be(0, &CmdFrm.operand[10], - CmdFrm.operand[12] - 1); - CmdFrm.operand[write_pos - 4] = (crc32_csum >> 24) & 0xFF; - CmdFrm.operand[write_pos - 3] = (crc32_csum >> 16) & 0xFF; - CmdFrm.operand[write_pos - 2] = (crc32_csum >> 8) & 0xFF; - CmdFrm.operand[write_pos - 1] = (crc32_csum >> 0) & 0xFF; - - CmdFrm.length = ALIGN(3 + write_pos, 4); - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if (RspFrm.resp != ACCEPTED) { - dev_err(&firesat->ud->device, - "CA PMT failed with response 0x%x\n", RspFrm.resp); - return -EFAULT; - } - - return 0; -} - -int avc_ca_get_time_date(struct firesat *firesat, int *interval) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; // ca tag - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - /* FIXME: check response code and validate response data */ - - *interval = RspFrm.operand[get_ca_object_pos(&RspFrm)]; - - return 0; -} - -int avc_ca_enter_menu(struct firesat *firesat) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - return 0; -} - -int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, unsigned int *len) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = firesat->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_MMI; - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) - return -EIO; - - /* FIXME: check response code and validate response data */ - - *len = get_ca_object_length(&RspFrm); - memcpy(mmi_object, &RspFrm.operand[get_ca_object_pos(&RspFrm)], *len); - - return 0; -} diff --git a/drivers/media/dvb/firesat/avc_api.h b/drivers/media/dvb/firesat/avc_api.h deleted file mode 100644 index 9d2efd8ff17b..000000000000 --- a/drivers/media/dvb/firesat/avc_api.h +++ /dev/null @@ -1,432 +0,0 @@ -/* - * AV/C API - * - * Copyright (C) 2000 Manfred Weihs - * Copyright (C) 2003 Philipp Gutgsell <0014guph@edu.fh-kaernten.ac.at> - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Ben Backx <ben@bbackx.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This is based on code written by Peter Halwachs, Thomas Groiss and - * Andreas Monitzer. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#ifndef _AVC_API_H -#define _AVC_API_H - -#include <linux/types.h> - -/************************************************************* - Constants from EN510221 -**************************************************************/ -#define LIST_MANAGEMENT_ONLY 0x03 - -/************************************************************ - definition of structures -*************************************************************/ -typedef struct { - int Nr_SourcePlugs; - int Nr_DestinationPlugs; -} TunerInfo; - - -/*********************************************** - - supported cts - -************************************************/ - -#define AVC 0x0 - -// FCP command frame with ctype = 0x0 is AVC command frame - -#ifdef __LITTLE_ENDIAN - -// Definition FCP Command Frame -typedef struct _AVCCmdFrm -{ - // AV/C command frame - __u8 ctype : 4 ; // command type - __u8 cts : 4 ; // always 0x0 for AVC - __u8 suid : 3 ; // subunit ID - __u8 sutyp : 5 ; // subunit_typ - __u8 opcode : 8 ; // opcode - __u8 operand[509] ; // array of operands [1-507] - int length; //length of the command frame -} AVCCmdFrm ; - -// Definition FCP Response Frame -typedef struct _AVCRspFrm -{ - // AV/C response frame - __u8 resp : 4 ; // response type - __u8 cts : 4 ; // always 0x0 for AVC - __u8 suid : 3 ; // subunit ID - __u8 sutyp : 5 ; // subunit_typ - __u8 opcode : 8 ; // opcode - __u8 operand[509] ; // array of operands [1-507] - int length; //length of the response frame -} AVCRspFrm ; - -#else - -typedef struct _AVCCmdFrm -{ - __u8 cts:4; - __u8 ctype:4; - __u8 sutyp:5; - __u8 suid:3; - __u8 opcode; - __u8 operand[509]; - int length; -} AVCCmdFrm; - -typedef struct _AVCRspFrm -{ - __u8 cts:4; - __u8 resp:4; - __u8 sutyp:5; - __u8 suid:3; - __u8 opcode; - __u8 operand[509]; - int length; -} AVCRspFrm; - -#endif - -/************************************************************* - AVC command types (ctype) -**************************************************************/// -#define CONTROL 0x00 -#define STATUS 0x01 -#define INQUIRY 0x02 -#define NOTIFY 0x03 - -/************************************************************* - AVC respond types -**************************************************************/// -#define NOT_IMPLEMENTED 0x8 -#define ACCEPTED 0x9 -#define REJECTED 0xA -#define STABLE 0xC -#define CHANGED 0xD -#define INTERIM 0xF - -/************************************************************* - AVC opcodes -**************************************************************/// -#define CONNECT 0x24 -#define DISCONNECT 0x25 -#define UNIT_INFO 0x30 -#define SUBUNIT_Info 0x31 -#define VENDOR 0x00 - -#define PLUG_INFO 0x02 -#define OPEN_DESCRIPTOR 0x08 -#define READ_DESCRIPTOR 0x09 -#define OBJECT_NUMBER_SELECT 0x0D - -/************************************************************* - AVCTuner opcodes -**************************************************************/ - -#define DSIT 0xC8 -#define DSD 0xCB -#define DESCRIPTOR_TUNER_STATUS 0x80 -#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 - -/************************************************************* - AVCTuner list types -**************************************************************/ -#define Multiplex_List 0x80 -#define Service_List 0x82 - -/************************************************************* - AVCTuner object entries -**************************************************************/ -#define Multiplex 0x80 -#define Service 0x82 -#define Service_with_specified_components 0x83 -#define Preferred_components 0x90 -#define Component 0x84 - -/************************************************************* - Vendor-specific commands -**************************************************************/ - -// digital everywhere vendor ID -#define SFE_VENDOR_DE_COMPANYID_0 0x00 -#define SFE_VENDOR_DE_COMPANYID_1 0x12 -#define SFE_VENDOR_DE_COMPANYID_2 0x87 - -#define SFE_VENDOR_MAX_NR_COMPONENTS 0x4 -#define SFE_VENDOR_MAX_NR_SERVICES 0x3 -#define SFE_VENDOR_MAX_NR_DSD_ELEMENTS 0x10 - -// vendor commands -#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0A -#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 -#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 // QPSK command for DVB-S - -// TODO: following vendor specific commands needs to be implemented -#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 -#define SFE_VENDOR_OPCODE_HOST2CA 0x56 -#define SFE_VENDOR_OPCODE_CA2HOST 0x57 -#define SFE_VENDOR_OPCODE_CISTATUS 0x59 -#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 // QPSK command for DVB-S2 devices - -// CA Tags -#define SFE_VENDOR_TAG_CA_RESET 0x00 -#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 -#define SFE_VENDOR_TAG_CA_PMT 0x02 -#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 -#define SFE_VENDOR_TAG_CA_MMI 0x05 -#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 - - -//AVCTuner DVB identifier service_ID -#define DVB 0x20 - -/************************************************************* - AVC descriptor types -**************************************************************/ - -#define Subunit_Identifier_Descriptor 0x00 -#define Tuner_Status_Descriptor 0x80 - -typedef struct { - __u8 Subunit_Type; - __u8 Max_Subunit_ID; -} SUBUNIT_INFO; - -/************************************************************* - - AVCTuner DVB object IDs are 6 byte long - -**************************************************************/ - -typedef struct { - __u8 Byte0; - __u8 Byte1; - __u8 Byte2; - __u8 Byte3; - __u8 Byte4; - __u8 Byte5; -}OBJECT_ID; - -/************************************************************* - MULIPLEX Structs -**************************************************************/ -typedef struct -{ -#ifdef __LITTLE_ENDIAN - __u8 RF_frequency_hByte:6; - __u8 raster_Frequency:2;//Bit7,6 raster frequency -#else - __u8 raster_Frequency:2; - __u8 RF_frequency_hByte:6; -#endif - __u8 RF_frequency_mByte; - __u8 RF_frequency_lByte; - -}FREQUENCY; - -#ifdef __LITTLE_ENDIAN - -typedef struct -{ - __u8 Modulation :1; - __u8 FEC_inner :1; - __u8 FEC_outer :1; - __u8 Symbol_Rate :1; - __u8 Frequency :1; - __u8 Orbital_Pos :1; - __u8 Polarisation :1; - __u8 reserved_fields :1; - __u8 reserved1 :7; - __u8 Network_ID :1; - -}MULTIPLEX_VALID_FLAGS; - -typedef struct -{ - __u8 GuardInterval:1; - __u8 CodeRateLPStream:1; - __u8 CodeRateHPStream:1; - __u8 HierarchyInfo:1; - __u8 Constellation:1; - __u8 Bandwidth:1; - __u8 CenterFrequency:1; - __u8 reserved1:1; - __u8 reserved2:5; - __u8 OtherFrequencyFlag:1; - __u8 TransmissionMode:1; - __u8 NetworkId:1; -}MULTIPLEX_VALID_FLAGS_DVBT; - -#else - -typedef struct { - __u8 reserved_fields:1; - __u8 Polarisation:1; - __u8 Orbital_Pos:1; - __u8 Frequency:1; - __u8 Symbol_Rate:1; - __u8 FEC_outer:1; - __u8 FEC_inner:1; - __u8 Modulation:1; - __u8 Network_ID:1; - __u8 reserved1:7; -}MULTIPLEX_VALID_FLAGS; - -typedef struct { - __u8 reserved1:1; - __u8 CenterFrequency:1; - __u8 Bandwidth:1; - __u8 Constellation:1; - __u8 HierarchyInfo:1; - __u8 CodeRateHPStream:1; - __u8 CodeRateLPStream:1; - __u8 GuardInterval:1; - __u8 NetworkId:1; - __u8 TransmissionMode:1; - __u8 OtherFrequencyFlag:1; - __u8 reserved2:5; -}MULTIPLEX_VALID_FLAGS_DVBT; - -#endif - -typedef union { - MULTIPLEX_VALID_FLAGS Bits; - MULTIPLEX_VALID_FLAGS_DVBT Bits_T; - struct { - __u8 ByteHi; - __u8 ByteLo; - } Valid_Word; -} M_VALID_FLAGS; - -typedef struct -{ -#ifdef __LITTLE_ENDIAN - __u8 ActiveSystem; - __u8 reserved:5; - __u8 NoRF:1; - __u8 Moving:1; - __u8 Searching:1; - - __u8 SelectedAntenna:7; - __u8 Input:1; - - __u8 BER[4]; - - __u8 SignalStrength; - FREQUENCY Frequency; - - __u8 ManDepInfoLength; - - __u8 PowerSupply:1; - __u8 FrontEndPowerStatus:1; - __u8 reserved3:1; - __u8 AntennaError:1; - __u8 FrontEndError:1; - __u8 reserved2:3; - - __u8 CarrierNoiseRatio[2]; - __u8 reserved4[2]; - __u8 PowerSupplyVoltage; - __u8 AntennaVoltage; - __u8 FirewireBusVoltage; - - __u8 CaMmi:1; - __u8 reserved5:7; - - __u8 reserved6:1; - __u8 CaInitializationStatus:1; - __u8 CaErrorFlag:1; - __u8 CaDvbFlag:1; - __u8 CaModulePresentStatus:1; - __u8 CaApplicationInfo:1; - __u8 CaDateTimeRequest:1; - __u8 CaPmtReply:1; - -#else - __u8 ActiveSystem; - __u8 Searching:1; - __u8 Moving:1; - __u8 NoRF:1; - __u8 reserved:5; - - __u8 Input:1; - __u8 SelectedAntenna:7; - - __u8 BER[4]; - - __u8 SignalStrength; - FREQUENCY Frequency; - - __u8 ManDepInfoLength; - - __u8 reserved2:3; - __u8 FrontEndError:1; - __u8 AntennaError:1; - __u8 reserved3:1; - __u8 FrontEndPowerStatus:1; - __u8 PowerSupply:1; - - __u8 CarrierNoiseRatio[2]; - __u8 reserved4[2]; - __u8 PowerSupplyVoltage; - __u8 AntennaVoltage; - __u8 FirewireBusVoltage; - - __u8 reserved5:7; - __u8 CaMmi:1; - __u8 CaPmtReply:1; - __u8 CaDateTimeRequest:1; - __u8 CaApplicationInfo:1; - __u8 CaModulePresentStatus:1; - __u8 CaDvbFlag:1; - __u8 CaErrorFlag:1; - __u8 CaInitializationStatus:1; - __u8 reserved6:1; - -#endif -} ANTENNA_INPUT_INFO; // 22 Byte - -#define LNBCONTROL_DONTCARE 0xff - -struct dvb_diseqc_master_cmd; -struct dvb_frontend_parameters; -struct firesat; - -int avc_recv(struct firesat *firesat, u8 *data, size_t length); - -int AVCTuner_DSIT(struct firesat *firesat, int Source_Plug, - struct dvb_frontend_parameters *params, __u8 *status); - -int avc_tuner_status(struct firesat *firesat, - ANTENNA_INPUT_INFO *antenna_input_info); -int avc_tuner_dsd(struct firesat *firesat, - struct dvb_frontend_parameters *params); -int avc_tuner_set_pids(struct firesat *firesat, unsigned char pidc, u16 pid[]); -int avc_tuner_get_ts(struct firesat *firesat); -int avc_identify_subunit(struct firesat *firesat); -int avc_lnb_control(struct firesat *firesat, char voltage, char burst, - char conttone, char nrdiseq, - struct dvb_diseqc_master_cmd *diseqcmd); -void avc_remote_ctrl_work(struct work_struct *work); -int avc_register_remote_control(struct firesat *firesat); -int avc_ca_app_info(struct firesat *firesat, char *app_info, unsigned int *len); -int avc_ca_info(struct firesat *firesat, char *app_info, unsigned int *len); -int avc_ca_reset(struct firesat *firesat); -int avc_ca_pmt(struct firesat *firesat, char *app_info, int length); -int avc_ca_get_time_date(struct firesat *firesat, int *interval); -int avc_ca_enter_menu(struct firesat *firesat); -int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, unsigned int *len); - -#endif /* _AVC_API_H */ diff --git a/drivers/media/dvb/firesat/cmp.c b/drivers/media/dvb/firesat/cmp.c deleted file mode 100644 index 8e98b814e430..000000000000 --- a/drivers/media/dvb/firesat/cmp.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/mutex.h> -#include <linux/types.h> - -#include <asm/byteorder.h> - -#include <ieee1394.h> -#include <nodemgr.h> - -#include "avc_api.h" -#include "cmp.h" -#include "firesat.h" - -#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL - -static int cmp_read(struct firesat *firesat, void *buf, u64 addr, size_t len) -{ - int ret; - - if (mutex_lock_interruptible(&firesat->avc_mutex)) - return -EINTR; - - ret = hpsb_node_read(firesat->ud->ne, addr, buf, len); - if (ret < 0) - dev_err(&firesat->ud->device, "CMP: read I/O error\n"); - - mutex_unlock(&firesat->avc_mutex); - return ret; -} - -static int cmp_lock(struct firesat *firesat, void *data, u64 addr, __be32 arg, - int ext_tcode) -{ - int ret; - - if (mutex_lock_interruptible(&firesat->avc_mutex)) - return -EINTR; - - ret = hpsb_node_lock(firesat->ud->ne, addr, ext_tcode, data, - (__force quadlet_t)arg); - if (ret < 0) - dev_err(&firesat->ud->device, "CMP: lock I/O error\n"); - - mutex_unlock(&firesat->avc_mutex); - return ret; -} - -static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) -{ - return (be32_to_cpu(opcr) >> shift) & mask; -} - -static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) -{ - *opcr &= ~cpu_to_be32(mask << shift); - *opcr |= cpu_to_be32((value & mask) << shift); -} - -#define get_opcr_online(v) get_opcr((v), 0x1, 31) -#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) -#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) - -#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) -#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) -#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) -#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) - -int cmp_establish_pp_connection(struct firesat *firesat, int plug, int channel) -{ - __be32 old_opcr, opcr; - u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); - int attempts = 0; - int ret; - - ret = cmp_read(firesat, &opcr, opcr_address, 4); - if (ret < 0) - return ret; - -repeat: - if (!get_opcr_online(opcr)) { - dev_err(&firesat->ud->device, "CMP: output offline\n"); - return -EBUSY; - } - - old_opcr = opcr; - - if (get_opcr_p2p_connections(opcr)) { - if (get_opcr_channel(opcr) != channel) { - dev_err(&firesat->ud->device, - "CMP: cannot change channel\n"); - return -EBUSY; - } - dev_info(&firesat->ud->device, - "CMP: overlaying existing connection\n"); - - /* We don't allocate isochronous resources. */ - } else { - set_opcr_channel(&opcr, channel); - set_opcr_data_rate(&opcr, IEEE1394_SPEED_400); - - /* FIXME: this is for the worst case - optimize */ - set_opcr_overhead_id(&opcr, 0); - - /* FIXME: allocate isochronous channel and bandwidth at IRM */ - } - - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); - - ret = cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2); - if (ret < 0) - return ret; - - if (old_opcr != opcr) { - /* - * FIXME: if old_opcr.P2P_Connections > 0, - * deallocate isochronous channel and bandwidth at IRM - */ - - if (++attempts < 6) /* arbitrary limit */ - goto repeat; - return -EBUSY; - } - - return 0; -} - -void cmp_break_pp_connection(struct firesat *firesat, int plug, int channel) -{ - __be32 old_opcr, opcr; - u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); - int attempts = 0; - - if (cmp_read(firesat, &opcr, opcr_address, 4) < 0) - return; - -repeat: - if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || - get_opcr_channel(opcr) != channel) { - dev_err(&firesat->ud->device, "CMP: no connection to break\n"); - return; - } - - old_opcr = opcr; - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); - - if (cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2) < 0) - return; - - if (old_opcr != opcr) { - /* - * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last - * owner, deallocate isochronous channel and bandwidth at IRM - */ - - if (++attempts < 6) /* arbitrary limit */ - goto repeat; - } -} diff --git a/drivers/media/dvb/firesat/cmp.h b/drivers/media/dvb/firesat/cmp.h deleted file mode 100644 index d92f6c7fb5d5..000000000000 --- a/drivers/media/dvb/firesat/cmp.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _CMP_H -#define _CMP_H - -struct firesat; - -int cmp_establish_pp_connection(struct firesat *firesat, int plug, int channel); -void cmp_break_pp_connection(struct firesat *firesat, int plug, int channel); - -#endif /* _CMP_H */ diff --git a/drivers/media/dvb/firesat/firesat-ci.c b/drivers/media/dvb/firesat/firesat-ci.c deleted file mode 100644 index 783ed2000102..000000000000 --- a/drivers/media/dvb/firesat/firesat-ci.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/dvb/ca.h> -#include <linux/fs.h> -#include <linux/module.h> - -#include <dvbdev.h> - -#include "avc_api.h" -#include "firesat.h" -#include "firesat-ci.h" - -static int firesat_ca_ready(ANTENNA_INPUT_INFO *info) -{ - return info->CaInitializationStatus == 1 && - info->CaErrorFlag == 0 && - info->CaDvbFlag == 1 && - info->CaModulePresentStatus == 1; -} - -static int firesat_get_ca_flags(ANTENNA_INPUT_INFO *info) -{ - int flags = 0; - - if (info->CaModulePresentStatus == 1) - flags |= CA_CI_MODULE_PRESENT; - if (info->CaInitializationStatus == 1 && - info->CaErrorFlag == 0 && - info->CaDvbFlag == 1) - flags |= CA_CI_MODULE_READY; - return flags; -} - -static int firesat_ca_reset(struct firesat *firesat) -{ - return avc_ca_reset(firesat) ? -EFAULT : 0; -} - -static int firesat_ca_get_caps(void *arg) -{ - struct ca_caps *cap = arg; - - cap->slot_num = 1; - cap->slot_type = CA_CI; - cap->descr_num = 1; - cap->descr_type = CA_ECD; - return 0; -} - -static int firesat_ca_get_slot_info(struct firesat *firesat, void *arg) -{ - ANTENNA_INPUT_INFO info; - struct ca_slot_info *slot = arg; - - if (avc_tuner_status(firesat, &info)) - return -EFAULT; - - if (slot->num != 0) - return -EFAULT; - - slot->type = CA_CI; - slot->flags = firesat_get_ca_flags(&info); - return 0; -} - -static int firesat_ca_app_info(struct firesat *firesat, void *arg) -{ - struct ca_msg *reply = arg; - - return - avc_ca_app_info(firesat, reply->msg, &reply->length) ? -EFAULT : 0; -} - -static int firesat_ca_info(struct firesat *firesat, void *arg) -{ - struct ca_msg *reply = arg; - - return avc_ca_info(firesat, reply->msg, &reply->length) ? -EFAULT : 0; -} - -static int firesat_ca_get_mmi(struct firesat *firesat, void *arg) -{ - struct ca_msg *reply = arg; - - return - avc_ca_get_mmi(firesat, reply->msg, &reply->length) ? -EFAULT : 0; -} - -static int firesat_ca_get_msg(struct firesat *firesat, void *arg) -{ - ANTENNA_INPUT_INFO info; - int err; - - switch (firesat->ca_last_command) { - case TAG_APP_INFO_ENQUIRY: - err = firesat_ca_app_info(firesat, arg); - break; - case TAG_CA_INFO_ENQUIRY: - err = firesat_ca_info(firesat, arg); - break; - default: - if (avc_tuner_status(firesat, &info)) - err = -EFAULT; - else if (info.CaMmi == 1) - err = firesat_ca_get_mmi(firesat, arg); - else { - printk(KERN_INFO "%s: Unhandled message 0x%08X\n", - __func__, firesat->ca_last_command); - err = -EFAULT; - } - } - firesat->ca_last_command = 0; - return err; -} - -static int firesat_ca_pmt(struct firesat *firesat, void *arg) -{ - struct ca_msg *msg = arg; - int data_pos; - int data_length; - int i; - - data_pos = 4; - if (msg->msg[3] & 0x80) { - data_length = 0; - for (i = 0; i < (msg->msg[3] & 0x7F); i++) - data_length = (data_length << 8) + msg->msg[data_pos++]; - } else { - data_length = msg->msg[3]; - } - - return avc_ca_pmt(firesat, &msg->msg[data_pos], data_length) ? - -EFAULT : 0; -} - -static int firesat_ca_send_msg(struct firesat *firesat, void *arg) -{ - struct ca_msg *msg = arg; - int err; - - /* Do we need a semaphore for this? */ - firesat->ca_last_command = - (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; - switch (firesat->ca_last_command) { - case TAG_CA_PMT: - err = firesat_ca_pmt(firesat, arg); - break; - case TAG_APP_INFO_ENQUIRY: - /* handled in ca_get_msg */ - err = 0; - break; - case TAG_CA_INFO_ENQUIRY: - /* handled in ca_get_msg */ - err = 0; - break; - case TAG_ENTER_MENU: - err = avc_ca_enter_menu(firesat); - break; - default: - printk(KERN_ERR "%s: Unhandled unknown message 0x%08X\n", - __func__, firesat->ca_last_command); - err = -EFAULT; - } - return err; -} - -static int firesat_ca_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct dvb_device *dvbdev = file->private_data; - struct firesat *firesat = dvbdev->priv; - ANTENNA_INPUT_INFO info; - int err; - - switch(cmd) { - case CA_RESET: - err = firesat_ca_reset(firesat); - break; - case CA_GET_CAP: - err = firesat_ca_get_caps(arg); - break; - case CA_GET_SLOT_INFO: - err = firesat_ca_get_slot_info(firesat, arg); - break; - case CA_GET_MSG: - err = firesat_ca_get_msg(firesat, arg); - break; - case CA_SEND_MSG: - err = firesat_ca_send_msg(firesat, arg); - break; - default: - printk(KERN_INFO "%s: Unhandled ioctl, command: %u\n",__func__, - cmd); - err = -EOPNOTSUPP; - } - - /* FIXME Is this necessary? */ - avc_tuner_status(firesat, &info); - - return err; -} - -static unsigned int firesat_ca_io_poll(struct file *file, poll_table *wait) -{ - return POLLIN; -} - -static struct file_operations firesat_ca_fops = { - .owner = THIS_MODULE, - .ioctl = dvb_generic_ioctl, - .open = dvb_generic_open, - .release = dvb_generic_release, - .poll = firesat_ca_io_poll, -}; - -static struct dvb_device firesat_ca = { - .users = 1, - .readers = 1, - .writers = 1, - .fops = &firesat_ca_fops, - .kernel_ioctl = firesat_ca_ioctl, -}; - -int firesat_ca_register(struct firesat *firesat) -{ - ANTENNA_INPUT_INFO info; - int err; - - if (avc_tuner_status(firesat, &info)) - return -EINVAL; - - if (!firesat_ca_ready(&info)) - return -EFAULT; - - err = dvb_register_device(&firesat->adapter, &firesat->cadev, - &firesat_ca, firesat, DVB_DEVICE_CA); - - if (info.CaApplicationInfo == 0) - printk(KERN_ERR "%s: CaApplicationInfo is not set.\n", - __func__); - if (info.CaDateTimeRequest == 1) - avc_ca_get_time_date(firesat, &firesat->ca_time_interval); - - return err; -} - -void firesat_ca_release(struct firesat *firesat) -{ - if (firesat->cadev) - dvb_unregister_device(firesat->cadev); -} diff --git a/drivers/media/dvb/firesat/firesat-ci.h b/drivers/media/dvb/firesat/firesat-ci.h deleted file mode 100644 index 9c68cd2246a7..000000000000 --- a/drivers/media/dvb/firesat/firesat-ci.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _FIREDTV_CI_H -#define _FIREDTV_CI_H - -struct firesat; - -int firesat_ca_register(struct firesat *firesat); -void firesat_ca_release(struct firesat *firesat); - -#endif /* _FIREDTV_CI_H */ diff --git a/drivers/media/dvb/firesat/firesat-rc.c b/drivers/media/dvb/firesat/firesat-rc.c deleted file mode 100644 index 5f9de142ee3e..000000000000 --- a/drivers/media/dvb/firesat/firesat-rc.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/bitops.h> -#include <linux/input.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/types.h> - -#include "firesat-rc.h" -#include "firesat.h" - -/* fixed table with older keycodes, geared towards MythTV */ -const static u16 oldtable[] = { - - /* code from device: 0x4501...0x451f */ - - KEY_ESC, - KEY_F9, - KEY_1, - KEY_2, - KEY_3, - KEY_4, - KEY_5, - KEY_6, - KEY_7, - KEY_8, - KEY_9, - KEY_I, - KEY_0, - KEY_ENTER, - KEY_RED, - KEY_UP, - KEY_GREEN, - KEY_F10, - KEY_SPACE, - KEY_F11, - KEY_YELLOW, - KEY_DOWN, - KEY_BLUE, - KEY_Z, - KEY_P, - KEY_PAGEDOWN, - KEY_LEFT, - KEY_W, - KEY_RIGHT, - KEY_P, - KEY_M, - - /* code from device: 0x4540...0x4542 */ - - KEY_R, - KEY_V, - KEY_C, -}; - -/* user-modifiable table for a remote as sold in 2008 */ -const static u16 keytable[] = { - - /* code from device: 0x0300...0x031f */ - - [0x00] = KEY_POWER, - [0x01] = KEY_SLEEP, - [0x02] = KEY_STOP, - [0x03] = KEY_OK, - [0x04] = KEY_RIGHT, - [0x05] = KEY_1, - [0x06] = KEY_2, - [0x07] = KEY_3, - [0x08] = KEY_LEFT, - [0x09] = KEY_4, - [0x0a] = KEY_5, - [0x0b] = KEY_6, - [0x0c] = KEY_UP, - [0x0d] = KEY_7, - [0x0e] = KEY_8, - [0x0f] = KEY_9, - [0x10] = KEY_DOWN, - [0x11] = KEY_TITLE, /* "OSD" - fixme */ - [0x12] = KEY_0, - [0x13] = KEY_F20, /* "16:9" - fixme */ - [0x14] = KEY_SCREEN, /* "FULL" - fixme */ - [0x15] = KEY_MUTE, - [0x16] = KEY_SUBTITLE, - [0x17] = KEY_RECORD, - [0x18] = KEY_TEXT, - [0x19] = KEY_AUDIO, - [0x1a] = KEY_RED, - [0x1b] = KEY_PREVIOUS, - [0x1c] = KEY_REWIND, - [0x1d] = KEY_PLAYPAUSE, - [0x1e] = KEY_NEXT, - [0x1f] = KEY_VOLUMEUP, - - /* code from device: 0x0340...0x0354 */ - - [0x20] = KEY_CHANNELUP, - [0x21] = KEY_F21, /* "4:3" - fixme */ - [0x22] = KEY_TV, - [0x23] = KEY_DVD, - [0x24] = KEY_VCR, - [0x25] = KEY_AUX, - [0x26] = KEY_GREEN, - [0x27] = KEY_YELLOW, - [0x28] = KEY_BLUE, - [0x29] = KEY_CHANNEL, /* "CH.LIST" */ - [0x2a] = KEY_VENDOR, /* "CI" - fixme */ - [0x2b] = KEY_VOLUMEDOWN, - [0x2c] = KEY_CHANNELDOWN, - [0x2d] = KEY_LAST, - [0x2e] = KEY_INFO, - [0x2f] = KEY_FORWARD, - [0x30] = KEY_LIST, - [0x31] = KEY_FAVORITES, - [0x32] = KEY_MENU, - [0x33] = KEY_EPG, - [0x34] = KEY_EXIT, -}; - -int firesat_register_rc(struct firesat *firesat, struct device *dev) -{ - struct input_dev *idev; - int i, err; - - idev = input_allocate_device(); - if (!idev) - return -ENOMEM; - - firesat->remote_ctrl_dev = idev; - idev->name = "FireDTV remote control"; - idev->dev.parent = dev; - idev->evbit[0] = BIT_MASK(EV_KEY); - idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); - if (!idev->keycode) { - err = -ENOMEM; - goto fail; - } - idev->keycodesize = sizeof(keytable[0]); - idev->keycodemax = ARRAY_SIZE(keytable); - - for (i = 0; i < ARRAY_SIZE(keytable); i++) - set_bit(keytable[i], idev->keybit); - - err = input_register_device(idev); - if (err) - goto fail_free_keymap; - - return 0; - -fail_free_keymap: - kfree(idev->keycode); -fail: - input_free_device(idev); - return err; -} - -void firesat_unregister_rc(struct firesat *firesat) -{ - kfree(firesat->remote_ctrl_dev->keycode); - input_unregister_device(firesat->remote_ctrl_dev); -} - -void firesat_handle_rc(struct firesat *firesat, unsigned int code) -{ - u16 *keycode = firesat->remote_ctrl_dev->keycode; - - if (code >= 0x0300 && code <= 0x031f) - code = keycode[code - 0x0300]; - else if (code >= 0x0340 && code <= 0x0354) - code = keycode[code - 0x0320]; - else if (code >= 0x4501 && code <= 0x451f) - code = oldtable[code - 0x4501]; - else if (code >= 0x4540 && code <= 0x4542) - code = oldtable[code - 0x4521]; - else { - printk(KERN_DEBUG "firedtv: invalid key code 0x%04x " - "from remote control\n", code); - return; - } - - input_report_key(firesat->remote_ctrl_dev, code, 1); - input_report_key(firesat->remote_ctrl_dev, code, 0); -} diff --git a/drivers/media/dvb/firesat/firesat-rc.h b/drivers/media/dvb/firesat/firesat-rc.h deleted file mode 100644 index 12c1c5c28b36..000000000000 --- a/drivers/media/dvb/firesat/firesat-rc.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _FIREDTV_RC_H -#define _FIREDTV_RC_H - -struct firesat; -struct device; - -int firesat_register_rc(struct firesat *firesat, struct device *dev); -void firesat_unregister_rc(struct firesat *firesat); -void firesat_handle_rc(struct firesat *firesat, unsigned int code); - -#endif /* _FIREDTV_RC_H */ diff --git a/drivers/media/dvb/firesat/firesat.h b/drivers/media/dvb/firesat/firesat.h deleted file mode 100644 index 51f64c0afcdb..000000000000 --- a/drivers/media/dvb/firesat/firesat.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#ifndef _FIREDTV_H -#define _FIREDTV_H - -#include <linux/dvb/dmx.h> -#include <linux/dvb/frontend.h> -#include <linux/list.h> -#include <linux/mutex.h> -#include <linux/spinlock_types.h> -#include <linux/types.h> -#include <linux/wait.h> -#include <linux/workqueue.h> - -#include <demux.h> -#include <dmxdev.h> -#include <dvb_demux.h> -#include <dvb_frontend.h> -#include <dvb_net.h> -#include <dvbdev.h> - -#include <linux/version.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) -#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w, v) -#else -#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w) -#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(x) -#endif - -/***************************************************************** - * CA message command constants from en50221_app_tags.h of libdvb - *****************************************************************/ -/* Resource Manager */ -#define TAG_PROFILE_ENQUIRY 0x9f8010 -#define TAG_PROFILE 0x9f8011 -#define TAG_PROFILE_CHANGE 0x9f8012 - -/* Application Info */ -#define TAG_APP_INFO_ENQUIRY 0x9f8020 -#define TAG_APP_INFO 0x9f8021 -#define TAG_ENTER_MENU 0x9f8022 - -/* CA Support */ -#define TAG_CA_INFO_ENQUIRY 0x9f8030 -#define TAG_CA_INFO 0x9f8031 -#define TAG_CA_PMT 0x9f8032 -#define TAG_CA_PMT_REPLY 0x9f8033 - -/* Host Control */ -#define TAG_TUNE 0x9f8400 -#define TAG_REPLACE 0x9f8401 -#define TAG_CLEAR_REPLACE 0x9f8402 -#define TAG_ASK_RELEASE 0x9f8403 - -/* Date and Time */ -#define TAG_DATE_TIME_ENQUIRY 0x9f8440 -#define TAG_DATE_TIME 0x9f8441 - -/* Man Machine Interface (MMI) */ -#define TAG_CLOSE_MMI 0x9f8800 -#define TAG_DISPLAY_CONTROL 0x9f8801 -#define TAG_DISPLAY_REPLY 0x9f8802 -#define TAG_TEXT_LAST 0x9f8803 -#define TAG_TEXT_MORE 0x9f8804 -#define TAG_KEYPAD_CONTROL 0x9f8805 -#define TAG_KEYPRESS 0x9f8806 -#define TAG_ENQUIRY 0x9f8807 -#define TAG_ANSWER 0x9f8808 -#define TAG_MENU_LAST 0x9f8809 -#define TAG_MENU_MORE 0x9f880a -#define TAG_MENU_ANSWER 0x9f880b -#define TAG_LIST_LAST 0x9f880c -#define TAG_LIST_MORE 0x9f880d -#define TAG_SUBTITLE_SEGMENT_LAST 0x9f880e -#define TAG_SUBTITLE_SEGMENT_MORE 0x9f880f -#define TAG_DISPLAY_MESSAGE 0x9f8810 -#define TAG_SCENE_END_MARK 0x9f8811 -#define TAG_SCENE_DONE 0x9f8812 -#define TAG_SCENE_CONTROL 0x9f8813 -#define TAG_SUBTITLE_DOWNLOAD_LAST 0x9f8814 -#define TAG_SUBTITLE_DOWNLOAD_MORE 0x9f8815 -#define TAG_FLUSH_DOWNLOAD 0x9f8816 -#define TAG_DOWNLOAD_REPLY 0x9f8817 - -/* Low Speed Communications */ -#define TAG_COMMS_COMMAND 0x9f8c00 -#define TAG_CONNECTION_DESCRIPTOR 0x9f8c01 -#define TAG_COMMS_REPLY 0x9f8c02 -#define TAG_COMMS_SEND_LAST 0x9f8c03 -#define TAG_COMMS_SEND_MORE 0x9f8c04 -#define TAG_COMMS_RECV_LAST 0x9f8c05 -#define TAG_COMMS_RECV_MORE 0x9f8c06 - -/* Authentication */ -#define TAG_AUTH_REQ 0x9f8200 -#define TAG_AUTH_RESP 0x9f8201 - -/* Teletext */ -#define TAG_TELETEXT_EBU 0x9f9000 - -/* Smartcard */ -#define TAG_SMARTCARD_COMMAND 0x9f8e00 -#define TAG_SMARTCARD_REPLY 0x9f8e01 -#define TAG_SMARTCARD_SEND 0x9f8e02 -#define TAG_SMARTCARD_RCV 0x9f8e03 - -/* EPG */ -#define TAG_EPG_ENQUIRY 0x9f8f00 -#define TAG_EPG_REPLY 0x9f8f01 - - -enum model_type { - FireSAT_UNKNOWN = 0, - FireSAT_DVB_S = 1, - FireSAT_DVB_C = 2, - FireSAT_DVB_T = 3, - FireSAT_DVB_S2 = 4, -}; - -struct input_dev; -struct hpsb_iso; -struct unit_directory; - -struct firesat { - struct dvb_adapter adapter; - struct dmxdev dmxdev; - struct dvb_demux demux; - struct dmx_frontend frontend; - struct dvb_net dvbnet; - struct dvb_frontend fe; - - struct dvb_device *cadev; - int ca_last_command; - int ca_time_interval; - - struct mutex avc_mutex; - wait_queue_head_t avc_wait; - bool avc_reply_received; - struct work_struct remote_ctrl_work; - struct input_dev *remote_ctrl_dev; - - struct firesat_channel { - bool active; - int pid; - } channel[16]; - struct mutex demux_mutex; - - struct unit_directory *ud; - - enum model_type type; - char subunit; - fe_sec_voltage_t voltage; - fe_sec_tone_mode_t tone; - - int isochannel; - struct hpsb_iso *iso_handle; - - struct list_head list; - - /* needed by avc_api */ - int resp_length; - u8 respfrm[512]; -}; - -struct firewireheader { - union { - struct { - __u8 tcode:4; - __u8 sy:4; - __u8 tag:2; - __u8 channel:6; - - __u8 length_l; - __u8 length_h; - } hdr; - __u32 val; - }; -}; - -struct CIPHeader { - union { - struct { - __u8 syncbits:2; - __u8 sid:6; - __u8 dbs; - __u8 fn:2; - __u8 qpc:3; - __u8 sph:1; - __u8 rsv:2; - __u8 dbc; - __u8 syncbits2:2; - __u8 fmt:6; - __u32 fdf:24; - } cip; - __u64 val; - }; -}; - -extern const char *firedtv_model_names[]; -extern struct list_head firesat_list; -extern spinlock_t firesat_list_lock; - -struct device; - -/* firesat_dvb.c */ -int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed); -int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed); -int firesat_dvbdev_init(struct firesat *firesat, struct device *dev); - -/* firesat_fe.c */ -void firesat_frontend_init(struct firesat *firesat); - -/* firesat_iso.c */ -int setup_iso_channel(struct firesat *firesat); -void tear_down_iso_channel(struct firesat *firesat); - -#endif /* _FIREDTV_H */ diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c deleted file mode 100644 index 11db62730256..000000000000 --- a/drivers/media/dvb/firesat/firesat_1394.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/string.h> -#include <linux/types.h> - -#include <dmxdev.h> -#include <dvb_demux.h> -#include <dvb_frontend.h> -#include <dvbdev.h> - -#include <csr1212.h> -#include <highlevel.h> -#include <hosts.h> -#include <ieee1394_hotplug.h> -#include <nodemgr.h> - -#include "avc_api.h" -#include "cmp.h" -#include "firesat.h" -#include "firesat-ci.h" -#include "firesat-rc.h" - -#define MATCH_FLAGS IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ - IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION -#define DIGITAL_EVERYWHERE_OUI 0x001287 - -static struct ieee1394_device_id firesat_id_table[] = { - - { - /* FloppyDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000024, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FloppyDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000025, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FloppyDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000026, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000034, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000035, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000036, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { } -}; - -MODULE_DEVICE_TABLE(ieee1394, firesat_id_table); - -/* list of all firesat devices */ -LIST_HEAD(firesat_list); -DEFINE_SPINLOCK(firesat_list_lock); - -static void fcp_request(struct hpsb_host *host, - int nodeid, - int direction, - int cts, - u8 *data, - size_t length) -{ - struct firesat *firesat = NULL; - struct firesat *firesat_entry; - unsigned long flags; - - if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { - - spin_lock_irqsave(&firesat_list_lock, flags); - list_for_each_entry(firesat_entry,&firesat_list,list) { - if (firesat_entry->ud->ne->host == host && - firesat_entry->ud->ne->nodeid == nodeid && - (firesat_entry->subunit == (data[1]&0x7) || - (firesat_entry->subunit == 0 && - (data[1]&0x7) == 0x7))) { - firesat=firesat_entry; - break; - } - } - spin_unlock_irqrestore(&firesat_list_lock, flags); - - if (firesat) - avc_recv(firesat, data, length); - } -} - -const char *firedtv_model_names[] = { - [FireSAT_UNKNOWN] = "unknown type", - [FireSAT_DVB_S] = "FireDTV S/CI", - [FireSAT_DVB_C] = "FireDTV C/CI", - [FireSAT_DVB_T] = "FireDTV T/CI", - [FireSAT_DVB_S2] = "FireDTV S2 ", -}; - -static int firesat_probe(struct device *dev) -{ - struct unit_directory *ud = - container_of(dev, struct unit_directory, device); - struct firesat *firesat; - unsigned long flags; - int kv_len; - void *kv_str; - int i; - int err = -ENOMEM; - - firesat = kzalloc(sizeof(*firesat), GFP_KERNEL); - if (!firesat) - return -ENOMEM; - - dev->driver_data = firesat; - firesat->ud = ud; - firesat->subunit = 0; - firesat->isochannel = -1; - firesat->tone = 0xff; - firesat->voltage = 0xff; - - mutex_init(&firesat->avc_mutex); - init_waitqueue_head(&firesat->avc_wait); - firesat->avc_reply_received = true; - mutex_init(&firesat->demux_mutex); - INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work); - - /* Reading device model from ROM */ - kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); - kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); - for (i = ARRAY_SIZE(firedtv_model_names); --i;) - if (strlen(firedtv_model_names[i]) <= kv_len && - strncmp(kv_str, firedtv_model_names[i], kv_len) == 0) - break; - firesat->type = i; - - /* - * Work around a bug in udev's path_id script: Use the fw-host's dev - * instead of the unit directory's dev as parent of the input device. - */ - err = firesat_register_rc(firesat, dev->parent->parent); - if (err) - goto fail_free; - - INIT_LIST_HEAD(&firesat->list); - spin_lock_irqsave(&firesat_list_lock, flags); - list_add_tail(&firesat->list, &firesat_list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - - err = avc_identify_subunit(firesat); - if (err) - goto fail; - - err = firesat_dvbdev_init(firesat, dev); - if (err) - goto fail; - - avc_register_remote_control(firesat); - return 0; - -fail: - spin_lock_irqsave(&firesat_list_lock, flags); - list_del(&firesat->list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - firesat_unregister_rc(firesat); -fail_free: - kfree(firesat); - return err; -} - -static int firesat_remove(struct device *dev) -{ - struct firesat *firesat = dev->driver_data; - unsigned long flags; - - firesat_ca_release(firesat); - dvb_unregister_frontend(&firesat->fe); - dvb_net_release(&firesat->dvbnet); - firesat->demux.dmx.close(&firesat->demux.dmx); - firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, - &firesat->frontend); - dvb_dmxdev_release(&firesat->dmxdev); - dvb_dmx_release(&firesat->demux); - dvb_unregister_adapter(&firesat->adapter); - - spin_lock_irqsave(&firesat_list_lock, flags); - list_del(&firesat->list); - spin_unlock_irqrestore(&firesat_list_lock, flags); - - cancel_work_sync(&firesat->remote_ctrl_work); - firesat_unregister_rc(firesat); - - kfree(firesat); - return 0; -} - -static int firesat_update(struct unit_directory *ud) -{ - struct firesat *firesat = ud->device.driver_data; - - if (firesat->isochannel >= 0) - cmp_establish_pp_connection(firesat, firesat->subunit, - firesat->isochannel); - return 0; -} - -static struct hpsb_protocol_driver firesat_driver = { - - .name = "firedtv", - .id_table = firesat_id_table, - .update = firesat_update, - - .driver = { - //.name and .bus are filled in for us in more recent linux versions - //.name = "FireSAT", - //.bus = &ieee1394_bus_type, - .probe = firesat_probe, - .remove = firesat_remove, - }, -}; - -static struct hpsb_highlevel firesat_highlevel = { - .name = "firedtv", - .fcp_request = fcp_request, -}; - -static int __init firesat_init(void) -{ - int ret; - - hpsb_register_highlevel(&firesat_highlevel); - ret = hpsb_register_protocol(&firesat_driver); - if (ret) { - printk(KERN_ERR "firedtv: failed to register protocol\n"); - hpsb_unregister_highlevel(&firesat_highlevel); - } - return ret; -} - -static void __exit firesat_exit(void) -{ - hpsb_unregister_protocol(&firesat_driver); - hpsb_unregister_highlevel(&firesat_highlevel); -} - -module_init(firesat_init); -module_exit(firesat_exit); - -MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); -MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); -MODULE_DESCRIPTION("FireDTV DVB Driver"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firesat/firesat_dvb.c b/drivers/media/dvb/firesat/firesat_dvb.c deleted file mode 100644 index cb36c0310199..000000000000 --- a/drivers/media/dvb/firesat/firesat_dvb.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/mutex.h> -#include <linux/types.h> - -#include <dvb_demux.h> -#include <dvb_frontend.h> -#include <dvbdev.h> - -#include "avc_api.h" -#include "firesat.h" -#include "firesat-ci.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static struct firesat_channel *firesat_channel_allocate(struct firesat *firesat) -{ - struct firesat_channel *c = NULL; - int k; - - if (mutex_lock_interruptible(&firesat->demux_mutex)) - return NULL; - - for (k = 0; k < 16; k++) - if (!firesat->channel[k].active) { - firesat->channel[k].active = true; - c = &firesat->channel[k]; - break; - } - - mutex_unlock(&firesat->demux_mutex); - return c; -} - -static int firesat_channel_collect(struct firesat *firesat, int *pidc, u16 pid[]) -{ - int k, l = 0; - - if (mutex_lock_interruptible(&firesat->demux_mutex)) - return -EINTR; - - for (k = 0; k < 16; k++) - if (firesat->channel[k].active) - pid[l++] = firesat->channel[k].pid; - - mutex_unlock(&firesat->demux_mutex); - - *pidc = l; - - return 0; -} - -static int firesat_channel_release(struct firesat *firesat, - struct firesat_channel *channel) -{ - if (mutex_lock_interruptible(&firesat->demux_mutex)) - return -EINTR; - - channel->active = false; - - mutex_unlock(&firesat->demux_mutex); - return 0; -} - -int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct firesat *firesat = (struct firesat*)dvbdmxfeed->demux->priv; - struct firesat_channel *channel; - int pidc,k; - u16 pids[16]; - - switch (dvbdmxfeed->type) { - case DMX_TYPE_TS: - case DMX_TYPE_SEC: - break; - default: - printk(KERN_ERR "%s: invalid type %u\n", - __func__, dvbdmxfeed->type); - return -EINVAL; - } - - if (dvbdmxfeed->type == DMX_TYPE_TS) { - switch (dvbdmxfeed->pes_type) { - case DMX_TS_PES_VIDEO: - case DMX_TS_PES_AUDIO: - case DMX_TS_PES_TELETEXT: - case DMX_TS_PES_PCR: - case DMX_TS_PES_OTHER: - //Dirty fix to keep firesat->channel pid-list up to date - for(k=0;k<16;k++){ - if (!firesat->channel[k].active) - firesat->channel[k].pid = - dvbdmxfeed->pid; - break; - } - channel = firesat_channel_allocate(firesat); - break; - default: - printk(KERN_ERR "%s: invalid pes type %u\n", - __func__, dvbdmxfeed->pes_type); - return -EINVAL; - } - } else { - channel = firesat_channel_allocate(firesat); - } - - if (!channel) { - printk(KERN_ERR "%s: busy!\n", __func__); - return -EBUSY; - } - - dvbdmxfeed->priv = channel; - channel->pid = dvbdmxfeed->pid; - - if (firesat_channel_collect(firesat, &pidc, pids)) { - firesat_channel_release(firesat, channel); - printk(KERN_ERR "%s: could not collect pids!\n", __func__); - return -EINTR; - } - - if (dvbdmxfeed->pid == 8192) { - k = avc_tuner_get_ts(firesat); - if (k) { - firesat_channel_release(firesat, channel); - printk("%s: AVCTuner_GetTS failed with error %d\n", - __func__, k); - return k; - } - } else { - k = avc_tuner_set_pids(firesat, pidc, pids); - if (k) { - firesat_channel_release(firesat, channel); - printk("%s: AVCTuner_SetPIDs failed with error %d\n", - __func__, k); - return k; - } - } - - return 0; -} - -int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *demux = dvbdmxfeed->demux; - struct firesat *firesat = (struct firesat*)demux->priv; - struct firesat_channel *c = dvbdmxfeed->priv; - int k, l; - u16 pids[16]; - - if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) && - (demux->dmx.frontend->source != DMX_MEMORY_FE))) { - - if (dvbdmxfeed->ts_type & TS_DECODER) { - - if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || - !demux->pesfilter[dvbdmxfeed->pes_type]) - - return -EINVAL; - - demux->pids[dvbdmxfeed->pes_type] |= 0x8000; - demux->pesfilter[dvbdmxfeed->pes_type] = NULL; - } - - if (!(dvbdmxfeed->ts_type & TS_DECODER && - dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) - - return 0; - } - - if (mutex_lock_interruptible(&firesat->demux_mutex)) - return -EINTR; - - /* list except channel to be removed */ - for (k = 0, l = 0; k < 16; k++) - if (firesat->channel[k].active) { - if (&firesat->channel[k] != c) - pids[l++] = firesat->channel[k].pid; - else - firesat->channel[k].active = false; - } - - k = avc_tuner_set_pids(firesat, l, pids); - if (!k) - c->active = false; - - mutex_unlock(&firesat->demux_mutex); - return k; -} - -int firesat_dvbdev_init(struct firesat *firesat, struct device *dev) -{ - int err; - - err = DVB_REGISTER_ADAPTER(&firesat->adapter, - firedtv_model_names[firesat->type], - THIS_MODULE, dev, adapter_nr); - if (err < 0) - goto fail_log; - - /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ - firesat->demux.dmx.capabilities = 0; - - firesat->demux.priv = (void *)firesat; - firesat->demux.filternum = 16; - firesat->demux.feednum = 16; - firesat->demux.start_feed = firesat_start_feed; - firesat->demux.stop_feed = firesat_stop_feed; - firesat->demux.write_to_decoder = NULL; - - err = dvb_dmx_init(&firesat->demux); - if (err) - goto fail_unreg_adapter; - - firesat->dmxdev.filternum = 16; - firesat->dmxdev.demux = &firesat->demux.dmx; - firesat->dmxdev.capabilities = 0; - - err = dvb_dmxdev_init(&firesat->dmxdev, &firesat->adapter); - if (err) - goto fail_dmx_release; - - firesat->frontend.source = DMX_FRONTEND_0; - - err = firesat->demux.dmx.add_frontend(&firesat->demux.dmx, - &firesat->frontend); - if (err) - goto fail_dmxdev_release; - - err = firesat->demux.dmx.connect_frontend(&firesat->demux.dmx, - &firesat->frontend); - if (err) - goto fail_rem_frontend; - - dvb_net_init(&firesat->adapter, &firesat->dvbnet, &firesat->demux.dmx); - - firesat_frontend_init(firesat); - err = dvb_register_frontend(&firesat->adapter, &firesat->fe); - if (err) - goto fail_net_release; - - err = firesat_ca_register(firesat); - if (err) - dev_info(dev, "Conditional Access Module not enabled\n"); - - return 0; - -fail_net_release: - dvb_net_release(&firesat->dvbnet); - firesat->demux.dmx.close(&firesat->demux.dmx); -fail_rem_frontend: - firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, - &firesat->frontend); -fail_dmxdev_release: - dvb_dmxdev_release(&firesat->dmxdev); -fail_dmx_release: - dvb_dmx_release(&firesat->demux); -fail_unreg_adapter: - dvb_unregister_adapter(&firesat->adapter); -fail_log: - dev_err(dev, "DVB initialization failed\n"); - return err; -} - - diff --git a/drivers/media/dvb/firesat/firesat_fe.c b/drivers/media/dvb/firesat/firesat_fe.c deleted file mode 100644 index 1ed972b79573..000000000000 --- a/drivers/media/dvb/firesat/firesat_fe.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/types.h> - -#include <dvb_frontend.h> - -#include "avc_api.h" -#include "cmp.h" -#include "firesat.h" - -static int firesat_dvb_init(struct dvb_frontend *fe) -{ - struct firesat *firesat = fe->sec_priv; - int err; - - /* FIXME - allocate free channel at IRM */ - firesat->isochannel = firesat->adapter.num; - - err = cmp_establish_pp_connection(firesat, firesat->subunit, - firesat->isochannel); - if (err) { - printk(KERN_ERR "Could not establish point to point " - "connection.\n"); - return err; - } - - return setup_iso_channel(firesat); -} - -static int firesat_sleep(struct dvb_frontend *fe) -{ - struct firesat *firesat = fe->sec_priv; - - tear_down_iso_channel(firesat); - cmp_break_pp_connection(firesat, firesat->subunit, firesat->isochannel); - firesat->isochannel = -1; - return 0; -} - -static int firesat_diseqc_send_master_cmd(struct dvb_frontend *fe, - struct dvb_diseqc_master_cmd *cmd) -{ - struct firesat *firesat = fe->sec_priv; - - return avc_lnb_control(firesat, LNBCONTROL_DONTCARE, - LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, 1, cmd); -} - -static int firesat_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t minicmd) -{ - return 0; -} - -static int firesat_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) -{ - struct firesat *firesat = fe->sec_priv; - - firesat->tone = tone; - return 0; -} - -static int firesat_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) -{ - struct firesat *firesat = fe->sec_priv; - - firesat->voltage = voltage; - return 0; -} - -static int firesat_read_status(struct dvb_frontend *fe, fe_status_t *status) -{ - struct firesat *firesat = fe->sec_priv; - ANTENNA_INPUT_INFO info; - - if (avc_tuner_status(firesat, &info)) - return -EINVAL; - - if (info.NoRF) - *status = 0; - else - *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | - FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; -} - -static int firesat_read_ber(struct dvb_frontend *fe, u32 *ber) -{ - struct firesat *firesat = fe->sec_priv; - ANTENNA_INPUT_INFO info; - - if (avc_tuner_status(firesat, &info)) - return -EINVAL; - - *ber = info.BER[0] << 24 | info.BER[1] << 16 | - info.BER[2] << 8 | info.BER[3]; - return 0; -} - -static int firesat_read_signal_strength (struct dvb_frontend *fe, u16 *strength) -{ - struct firesat *firesat = fe->sec_priv; - ANTENNA_INPUT_INFO info; - - if (avc_tuner_status(firesat, &info)) - return -EINVAL; - - *strength = info.SignalStrength << 8; - return 0; -} - -static int firesat_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct firesat *firesat = fe->sec_priv; - ANTENNA_INPUT_INFO info; - - if (avc_tuner_status(firesat, &info)) - return -EINVAL; - - /* C/N[dB] = -10 * log10(snr / 65535) */ - *snr = (info.CarrierNoiseRatio[0] << 8) + info.CarrierNoiseRatio[1]; - *snr *= 257; - return 0; -} - -static int firesat_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) -{ - return -EOPNOTSUPP; -} - -static int firesat_set_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - struct firesat *firesat = fe->sec_priv; - - /* FIXME: avc_tuner_dsd never returns ACCEPTED. Check status? */ - if (avc_tuner_dsd(firesat, params) != ACCEPTED) - return -EINVAL; - else - return 0; /* not sure of this... */ -} - -static int firesat_get_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - return -EOPNOTSUPP; -} - -void firesat_frontend_init(struct firesat *firesat) -{ - struct dvb_frontend_ops *ops = &firesat->fe.ops; - struct dvb_frontend_info *fi = &ops->info; - - ops->init = firesat_dvb_init; - ops->sleep = firesat_sleep; - - ops->set_frontend = firesat_set_frontend; - ops->get_frontend = firesat_get_frontend; - - ops->read_status = firesat_read_status; - ops->read_ber = firesat_read_ber; - ops->read_signal_strength = firesat_read_signal_strength; - ops->read_snr = firesat_read_snr; - ops->read_ucblocks = firesat_read_uncorrected_blocks; - - ops->diseqc_send_master_cmd = firesat_diseqc_send_master_cmd; - ops->diseqc_send_burst = firesat_diseqc_send_burst; - ops->set_tone = firesat_set_tone; - ops->set_voltage = firesat_set_voltage; - - switch (firesat->type) { - case FireSAT_DVB_S: - fi->type = FE_QPSK; - - fi->frequency_min = 950000; - fi->frequency_max = 2150000; - fi->frequency_stepsize = 125; - fi->symbol_rate_min = 1000000; - fi->symbol_rate_max = 40000000; - - fi->caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK; - break; - - case FireSAT_DVB_C: - fi->type = FE_QAM; - - fi->frequency_min = 47000000; - fi->frequency_max = 866000000; - fi->frequency_stepsize = 62500; - fi->symbol_rate_min = 870000; - fi->symbol_rate_max = 6900000; - - fi->caps = FE_CAN_INVERSION_AUTO | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_QAM_AUTO; - break; - - case FireSAT_DVB_T: - fi->type = FE_OFDM; - - fi->frequency_min = 49000000; - fi->frequency_max = 861000000; - fi->frequency_stepsize = 62500; - - fi->caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_2_3 | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO; - break; - - default: - printk(KERN_ERR "FireDTV: no frontend for model type %d\n", - firesat->type); - } - strcpy(fi->name, firedtv_model_names[firesat->type]); - - firesat->fe.dvb = &firesat->adapter; - firesat->fe.sec_priv = firesat; -} diff --git a/drivers/media/dvb/firesat/firesat_iso.c b/drivers/media/dvb/firesat/firesat_iso.c deleted file mode 100644 index b3c61f95fa94..000000000000 --- a/drivers/media/dvb/firesat/firesat_iso.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * FireSAT DVB driver - * - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/spinlock.h> - -#include <dvb_demux.h> - -#include <dma.h> -#include <iso.h> -#include <nodemgr.h> - -#include "firesat.h" - -static void rawiso_activity_cb(struct hpsb_iso *iso); - -void tear_down_iso_channel(struct firesat *firesat) -{ - if (firesat->iso_handle != NULL) { - hpsb_iso_stop(firesat->iso_handle); - hpsb_iso_shutdown(firesat->iso_handle); - } - firesat->iso_handle = NULL; -} - -int setup_iso_channel(struct firesat *firesat) -{ - int result; - firesat->iso_handle = - hpsb_iso_recv_init(firesat->ud->ne->host, - 256 * 200, //data_buf_size, - 256, //buf_packets, - firesat->isochannel, - HPSB_ISO_DMA_DEFAULT, //dma_mode, - -1, //stat.config.irq_interval, - rawiso_activity_cb); - if (firesat->iso_handle == NULL) { - printk(KERN_ERR "Cannot initialize iso receive.\n"); - return -EINVAL; - } - result = hpsb_iso_recv_start(firesat->iso_handle, -1, -1, 0); - if (result != 0) { - printk(KERN_ERR "Cannot start iso receive.\n"); - return -EINVAL; - } - return 0; -} - -static void rawiso_activity_cb(struct hpsb_iso *iso) -{ - unsigned int num; - unsigned int i; - unsigned int packet; - unsigned long flags; - struct firesat *firesat = NULL; - struct firesat *firesat_iterator; - - spin_lock_irqsave(&firesat_list_lock, flags); - list_for_each_entry(firesat_iterator, &firesat_list, list) { - if(firesat_iterator->iso_handle == iso) { - firesat = firesat_iterator; - break; - } - } - spin_unlock_irqrestore(&firesat_list_lock, flags); - - if (firesat) { - packet = iso->first_packet; - num = hpsb_iso_n_ready(iso); - for (i = 0; i < num; i++, - packet = (packet + 1) % iso->buf_packets) { - unsigned char *buf = - dma_region_i(&iso->data_buf, unsigned char, - iso->infos[packet].offset + - sizeof(struct CIPHeader)); - int count = (iso->infos[packet].len - - sizeof(struct CIPHeader)) / - (188 + sizeof(struct firewireheader)); - if (iso->infos[packet].len <= sizeof(struct CIPHeader)) - continue; // ignore empty packet - - while (count --) { - if (buf[sizeof(struct firewireheader)] == 0x47) - dvb_dmx_swfilter_packets(&firesat->demux, - &buf[sizeof(struct firewireheader)], 1); - else - printk("%s: invalid packet, skipping\n", __func__); - buf += 188 + sizeof(struct firewireheader); - - } - - } - hpsb_iso_recv_release_packets(iso, num); - } - else { - printk("%s: packets for unknown iso channel, skipping\n", - __func__); - hpsb_iso_recv_release_packets(iso, hpsb_iso_n_ready(iso)); - } -} - diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig new file mode 100644 index 000000000000..03d25ad10350 --- /dev/null +++ b/drivers/media/dvb/firewire/Kconfig @@ -0,0 +1,12 @@ +config DVB_FIREDTV + tristate "FireDTV (FireWire attached DVB receivers)" + depends on DVB_CORE && IEEE1394 && INPUT + help + Support for DVB receivers from Digital Everywhere, known as FireDTV + and FloppyDTV, which are connected via IEEE 1394 (FireWire). + + These devices don't have an MPEG decoder built in, so you need + an external software decoder to watch TV. + + To compile this driver as a module, say M here: the module will be + called firedtv. diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile new file mode 100644 index 000000000000..628dacd10daf --- /dev/null +++ b/drivers/media/dvb/firewire/Makefile @@ -0,0 +1,13 @@ +firedtv-objs := firedtv-1394.o \ + firedtv-dvb.o \ + firedtv-fe.o \ + firedtv-iso.o \ + avc.o \ + cmp.o \ + firedtv-rc.o \ + firedtv-ci.o + +obj-$(CONFIG_DVB_FIREDTV) += firedtv.o + +EXTRA_CFLAGS := -Idrivers/ieee1394 +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/dvb/firewire/avc.c b/drivers/media/dvb/firewire/avc.c new file mode 100644 index 000000000000..847a537b1f58 --- /dev/null +++ b/drivers/media/dvb/firewire/avc.c @@ -0,0 +1,1051 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/bug.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include <ieee1394_transactions.h> +#include <nodemgr.h> + +#include "avc.h" +#include "firedtv.h" +#include "firedtv-rc.h" + +#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL + +static int __avc_write(struct firedtv *fdtv, + const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) +{ + int err, retry; + + if (RspFrm) + fdtv->avc_reply_received = false; + + for (retry = 0; retry < 6; retry++) { + err = hpsb_node_write(fdtv->ud->ne, FCP_COMMAND_REGISTER, + (quadlet_t *)CmdFrm, CmdFrm->length); + if (err) { + fdtv->avc_reply_received = true; + dev_err(&fdtv->ud->device, + "FCP command write failed\n"); + return err; + } + + if (!RspFrm) + return 0; + + /* + * AV/C specs say that answers should be sent within 150 ms. + * Time out after 200 ms. + */ + if (wait_event_timeout(fdtv->avc_wait, + fdtv->avc_reply_received, + HZ / 5) != 0) { + memcpy(RspFrm, fdtv->respfrm, fdtv->resp_length); + RspFrm->length = fdtv->resp_length; + + return 0; + } + } + dev_err(&fdtv->ud->device, "FCP response timed out\n"); + return -ETIMEDOUT; +} + +static int avc_write(struct firedtv *fdtv, + const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = __avc_write(fdtv, CmdFrm, RspFrm); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +int avc_recv(struct firedtv *fdtv, u8 *data, size_t length) +{ + AVCRspFrm *RspFrm = (AVCRspFrm *)data; + + if (length >= 8 && + RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && + RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && + RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && + RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { + if (RspFrm->resp == CHANGED) { + fdtv_handle_rc(fdtv, + RspFrm->operand[4] << 8 | RspFrm->operand[5]); + schedule_work(&fdtv->remote_ctrl_work); + } else if (RspFrm->resp != INTERIM) { + dev_info(&fdtv->ud->device, + "remote control result = %d\n", RspFrm->resp); + } + return 0; + } + + if (fdtv->avc_reply_received) { + dev_err(&fdtv->ud->device, + "received out-of-order AVC response, ignored\n"); + return -EIO; + } + + memcpy(fdtv->respfrm, data, length); + fdtv->resp_length = length; + + fdtv->avc_reply_received = true; + wake_up(&fdtv->avc_wait); + + return 0; +} + +/* + * tuning command for setting the relative LNB frequency + * (not supported by the AVC standard) + */ +static void avc_tuner_tuneqpsk(struct firedtv *fdtv, + struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) +{ + CmdFrm->opcode = VENDOR; + + CmdFrm->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + CmdFrm->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + CmdFrm->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + CmdFrm->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; + + CmdFrm->operand[4] = (params->frequency >> 24) & 0xff; + CmdFrm->operand[5] = (params->frequency >> 16) & 0xff; + CmdFrm->operand[6] = (params->frequency >> 8) & 0xff; + CmdFrm->operand[7] = params->frequency & 0xff; + + CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; + CmdFrm->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; + + switch(params->u.qpsk.fec_inner) { + case FEC_1_2: + CmdFrm->operand[10] = 0x1; break; + case FEC_2_3: + CmdFrm->operand[10] = 0x2; break; + case FEC_3_4: + CmdFrm->operand[10] = 0x3; break; + case FEC_5_6: + CmdFrm->operand[10] = 0x4; break; + case FEC_7_8: + CmdFrm->operand[10] = 0x5; break; + case FEC_4_5: + case FEC_8_9: + case FEC_AUTO: + default: + CmdFrm->operand[10] = 0x0; + } + + if (fdtv->voltage == 0xff) + CmdFrm->operand[11] = 0xff; + else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ + CmdFrm->operand[11] = 0; + else + CmdFrm->operand[11] = 1; + + if (fdtv->tone == 0xff) + CmdFrm->operand[12] = 0xff; + else if (fdtv->tone == SEC_TONE_ON) /* band */ + CmdFrm->operand[12] = 1; + else + CmdFrm->operand[12] = 0; + + if (fdtv->type == FIREDTV_DVB_S2) { + CmdFrm->operand[13] = 0x1; + CmdFrm->operand[14] = 0xff; + CmdFrm->operand[15] = 0xff; + CmdFrm->length = 20; + } else { + CmdFrm->length = 16; + } +} + +static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, + AVCCmdFrm *CmdFrm) +{ + M_VALID_FLAGS flags; + + flags.Bits.Modulation = params->u.qam.modulation != QAM_AUTO; + flags.Bits.FEC_inner = params->u.qam.fec_inner != FEC_AUTO; + flags.Bits.FEC_outer = 0; + flags.Bits.Symbol_Rate = 1; + flags.Bits.Frequency = 1; + flags.Bits.Orbital_Pos = 0; + flags.Bits.Polarisation = 0; + flags.Bits.reserved_fields = 0; + flags.Bits.reserved1 = 0; + flags.Bits.Network_ID = 0; + + CmdFrm->opcode = DSD; + + CmdFrm->operand[0] = 0; /* source plug */ + CmdFrm->operand[1] = 0xd2; /* subfunction replace */ + CmdFrm->operand[2] = 0x20; /* system id = DVB */ + CmdFrm->operand[3] = 0x00; /* antenna number */ + /* system_specific_multiplex selection_length */ + CmdFrm->operand[4] = 0x11; + CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ + CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ + CmdFrm->operand[7] = 0x00; + CmdFrm->operand[8] = 0x00; + CmdFrm->operand[9] = 0x00; + CmdFrm->operand[10] = 0x00; + + CmdFrm->operand[11] = + (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); + CmdFrm->operand[12] = + ((params->frequency / 4000) >> 8) & 0xff; + CmdFrm->operand[13] = (params->frequency / 4000) & 0xff; + CmdFrm->operand[14] = + ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; + CmdFrm->operand[15] = + ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; + CmdFrm->operand[16] = + ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; + CmdFrm->operand[17] = 0x00; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: + CmdFrm->operand[18] = 0x1; break; + case FEC_2_3: + CmdFrm->operand[18] = 0x2; break; + case FEC_3_4: + CmdFrm->operand[18] = 0x3; break; + case FEC_5_6: + CmdFrm->operand[18] = 0x4; break; + case FEC_7_8: + CmdFrm->operand[18] = 0x5; break; + case FEC_8_9: + CmdFrm->operand[18] = 0x6; break; + case FEC_4_5: + CmdFrm->operand[18] = 0x8; break; + case FEC_AUTO: + default: + CmdFrm->operand[18] = 0x0; + } + switch (params->u.qam.modulation) { + case QAM_16: + CmdFrm->operand[19] = 0x08; break; + case QAM_32: + CmdFrm->operand[19] = 0x10; break; + case QAM_64: + CmdFrm->operand[19] = 0x18; break; + case QAM_128: + CmdFrm->operand[19] = 0x20; break; + case QAM_256: + CmdFrm->operand[19] = 0x28; break; + case QAM_AUTO: + default: + CmdFrm->operand[19] = 0x00; + } + CmdFrm->operand[20] = 0x00; + CmdFrm->operand[21] = 0x00; + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + CmdFrm->operand[22] = 0x00; + + CmdFrm->length = 28; +} + +static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, + AVCCmdFrm *CmdFrm) +{ + M_VALID_FLAGS flags; + + flags.Bits_T.GuardInterval = + params->u.ofdm.guard_interval != GUARD_INTERVAL_AUTO; + flags.Bits_T.CodeRateLPStream = + params->u.ofdm.code_rate_LP != FEC_AUTO; + flags.Bits_T.CodeRateHPStream = + params->u.ofdm.code_rate_HP != FEC_AUTO; + flags.Bits_T.HierarchyInfo = + params->u.ofdm.hierarchy_information != HIERARCHY_AUTO; + flags.Bits_T.Constellation = + params->u.ofdm.constellation != QAM_AUTO; + flags.Bits_T.Bandwidth = + params->u.ofdm.bandwidth != BANDWIDTH_AUTO; + flags.Bits_T.CenterFrequency = 1; + flags.Bits_T.reserved1 = 0; + flags.Bits_T.reserved2 = 0; + flags.Bits_T.OtherFrequencyFlag = 0; + flags.Bits_T.TransmissionMode = + params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO; + flags.Bits_T.NetworkId = 0; + + CmdFrm->opcode = DSD; + + CmdFrm->operand[0] = 0; /* source plug */ + CmdFrm->operand[1] = 0xd2; /* subfunction replace */ + CmdFrm->operand[2] = 0x20; /* system id = DVB */ + CmdFrm->operand[3] = 0x00; /* antenna number */ + /* system_specific_multiplex selection_length */ + CmdFrm->operand[4] = 0x0c; + CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ + CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ + CmdFrm->operand[7] = 0x0; + CmdFrm->operand[8] = (params->frequency / 10) >> 24; + CmdFrm->operand[9] = ((params->frequency / 10) >> 16) & 0xff; + CmdFrm->operand[10] = ((params->frequency / 10) >> 8) & 0xff; + CmdFrm->operand[11] = (params->frequency / 10) & 0xff; + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_7_MHZ: + CmdFrm->operand[12] = 0x20; break; + case BANDWIDTH_8_MHZ: + case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ + case BANDWIDTH_AUTO: + default: + CmdFrm->operand[12] = 0x00; + } + switch (params->u.ofdm.constellation) { + case QAM_16: + CmdFrm->operand[13] = 1 << 6; break; + case QAM_64: + CmdFrm->operand[13] = 2 << 6; break; + case QPSK: + default: + CmdFrm->operand[13] = 0x00; + } + switch (params->u.ofdm.hierarchy_information) { + case HIERARCHY_1: + CmdFrm->operand[13] |= 1 << 3; break; + case HIERARCHY_2: + CmdFrm->operand[13] |= 2 << 3; break; + case HIERARCHY_4: + CmdFrm->operand[13] |= 3 << 3; break; + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + default: + break; + } + switch (params->u.ofdm.code_rate_HP) { + case FEC_2_3: + CmdFrm->operand[13] |= 1; break; + case FEC_3_4: + CmdFrm->operand[13] |= 2; break; + case FEC_5_6: + CmdFrm->operand[13] |= 3; break; + case FEC_7_8: + CmdFrm->operand[13] |= 4; break; + case FEC_1_2: + default: + break; + } + switch (params->u.ofdm.code_rate_LP) { + case FEC_2_3: + CmdFrm->operand[14] = 1 << 5; break; + case FEC_3_4: + CmdFrm->operand[14] = 2 << 5; break; + case FEC_5_6: + CmdFrm->operand[14] = 3 << 5; break; + case FEC_7_8: + CmdFrm->operand[14] = 4 << 5; break; + case FEC_1_2: + default: + CmdFrm->operand[14] = 0x00; break; + } + switch (params->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_16: + CmdFrm->operand[14] |= 1 << 3; break; + case GUARD_INTERVAL_1_8: + CmdFrm->operand[14] |= 2 << 3; break; + case GUARD_INTERVAL_1_4: + CmdFrm->operand[14] |= 3 << 3; break; + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + default: + break; + } + switch (params->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_8K: + CmdFrm->operand[14] |= 1 << 1; break; + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + default: + break; + } + + CmdFrm->operand[15] = 0x00; /* network_ID[0] */ + CmdFrm->operand[16] = 0x00; /* network_ID[1] */ + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + CmdFrm->operand[17] = 0x00; + + CmdFrm->length = 24; +} + +int avc_tuner_dsd(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + + switch (fdtv->type) { + case FIREDTV_DVB_S: + case FIREDTV_DVB_S2: + avc_tuner_tuneqpsk(fdtv, params, &CmdFrm); break; + case FIREDTV_DVB_C: + avc_tuner_dsd_dvb_c(params, &CmdFrm); break; + case FIREDTV_DVB_T: + avc_tuner_dsd_dvb_t(params, &CmdFrm); break; + default: + BUG(); + } + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + msleep(500); +#if 0 + /* FIXME: */ + /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ + if(status) + *status=RspFrm.operand[2]; +#endif + return 0; +} + +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int pos, k; + + if (pidc > 16 && pidc != 0xff) + return -EINVAL; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = DSD; + + CmdFrm.operand[0] = 0; // source plug + CmdFrm.operand[1] = 0xD2; // subfunction replace + CmdFrm.operand[2] = 0x20; // system id = DVB + CmdFrm.operand[3] = 0x00; // antenna number + CmdFrm.operand[4] = 0x00; // system_specific_multiplex selection_length + CmdFrm.operand[5] = pidc; // Nr_of_dsd_sel_specs + + pos = 6; + if (pidc != 0xff) + for (k = 0; k < pidc; k++) { + CmdFrm.operand[pos++] = 0x13; // flowfunction relay + CmdFrm.operand[pos++] = 0x80; // dsd_sel_spec_valid_flags -> PID + CmdFrm.operand[pos++] = (pid[k] >> 8) & 0x1F; + CmdFrm.operand[pos++] = pid[k] & 0xFF; + CmdFrm.operand[pos++] = 0x00; // tableID + CmdFrm.operand[pos++] = 0x00; // filter_length + } + + CmdFrm.length = ALIGN(3 + pos, 4); + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + msleep(50); + return 0; +} + +int avc_tuner_get_ts(struct firedtv *fdtv) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = DSIT; + + CmdFrm.operand[0] = 0; // source plug + CmdFrm.operand[1] = 0xD2; // subfunction replace + CmdFrm.operand[2] = 0xFF; //status + CmdFrm.operand[3] = 0x20; // system id = DVB + CmdFrm.operand[4] = 0x00; // antenna number + CmdFrm.operand[5] = 0x0; // system_specific_search_flags + CmdFrm.operand[6] = (fdtv->type == FIREDTV_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length + CmdFrm.operand[7] = 0x00; // valid_flags [0] + CmdFrm.operand[8] = 0x00; // valid_flags [1] + CmdFrm.operand[7 + (fdtv->type == FIREDTV_DVB_T)?0x0c:0x11] = 0x00; // nr_of_dsit_sel_specs (always 0) + + CmdFrm.length = (fdtv->type == FIREDTV_DVB_T)?24:28; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + msleep(250); + return 0; +} + +int avc_identify_subunit(struct firedtv *fdtv) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm,0,sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; // tuner + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = READ_DESCRIPTOR; + + CmdFrm.operand[0]=DESCRIPTOR_SUBUNIT_IDENTIFIER; + CmdFrm.operand[1]=0xff; + CmdFrm.operand[2]=0x00; + CmdFrm.operand[3]=0x00; // length highbyte + CmdFrm.operand[4]=0x08; // length lowbyte + CmdFrm.operand[5]=0x00; // offset highbyte + CmdFrm.operand[6]=0x0d; // offset lowbyte + + CmdFrm.length=12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + if ((RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) || + (RspFrm.operand[3] << 8) + RspFrm.operand[4] != 8) { + dev_err(&fdtv->ud->device, + "cannot read subunit identifier\n"); + return -EINVAL; + } + return 0; +} + +int avc_tuner_status(struct firedtv *fdtv, + ANTENNA_INPUT_INFO *antenna_input_info) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int length; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts=AVC; + CmdFrm.ctype=CONTROL; + CmdFrm.sutyp=0x05; // tuner + CmdFrm.suid=fdtv->subunit; + CmdFrm.opcode=READ_DESCRIPTOR; + + CmdFrm.operand[0]=DESCRIPTOR_TUNER_STATUS; + CmdFrm.operand[1]=0xff; //read_result_status + CmdFrm.operand[2]=0x00; // reserver + CmdFrm.operand[3]=0;//sizeof(ANTENNA_INPUT_INFO) >> 8; + CmdFrm.operand[4]=0;//sizeof(ANTENNA_INPUT_INFO) & 0xFF; + CmdFrm.operand[5]=0x00; + CmdFrm.operand[6]=0x00; + CmdFrm.length=12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + if (RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { + dev_err(&fdtv->ud->device, "cannot read tuner status\n"); + return -EINVAL; + } + + length = RspFrm.operand[9]; + if (RspFrm.operand[1] != 0x10 || length != sizeof(ANTENNA_INPUT_INFO)) { + dev_err(&fdtv->ud->device, "got invalid tuner status\n"); + return -EINVAL; + } + + memcpy(antenna_input_info, &RspFrm.operand[10], length); + return 0; +} + +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int i, j, k; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts=AVC; + CmdFrm.ctype=CONTROL; + CmdFrm.sutyp=0x05; + CmdFrm.suid=fdtv->subunit; + CmdFrm.opcode=VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_LNB_CONTROL; + + CmdFrm.operand[4]=voltage; + CmdFrm.operand[5]=nrdiseq; + + i=6; + + for (j = 0; j < nrdiseq; j++) { + CmdFrm.operand[i++] = diseqcmd[j].msg_len; + + for (k = 0; k < diseqcmd[j].msg_len; k++) + CmdFrm.operand[i++] = diseqcmd[j].msg[k]; + } + + CmdFrm.operand[i++]=burst; + CmdFrm.operand[i++]=conttone; + + CmdFrm.length = ALIGN(3 + i, 4); + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + if (RspFrm.resp != ACCEPTED) { + dev_err(&fdtv->ud->device, "LNB control failed\n"); + return -EINVAL; + } + + return 0; +} + +int avc_register_remote_control(struct firedtv *fdtv) +{ + AVCCmdFrm CmdFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + + CmdFrm.cts = AVC; + CmdFrm.ctype = NOTIFY; + CmdFrm.sutyp = 0x1f; + CmdFrm.suid = 0x7; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0] = SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1] = SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2] = SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; + + CmdFrm.length = 8; + + return avc_write(fdtv, &CmdFrm, NULL); +} + +void avc_remote_ctrl_work(struct work_struct *work) +{ + struct firedtv *fdtv = + container_of(work, struct firedtv, remote_ctrl_work); + + /* Should it be rescheduled in failure cases? */ + avc_register_remote_control(fdtv); +} + +#if 0 /* FIXME: unused */ +int avc_tuner_host2ca(struct firedtv *fdtv) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + return 0; +} +#endif + +static int get_ca_object_pos(AVCRspFrm *RspFrm) +{ + int length = 1; + + /* Check length of length field */ + if (RspFrm->operand[7] & 0x80) + length = (RspFrm->operand[7] & 0x7f) + 1; + return length + 7; +} + +static int get_ca_object_length(AVCRspFrm *RspFrm) +{ +#if 0 /* FIXME: unused */ + int size = 0; + int i; + + if (RspFrm->operand[7] & 0x80) + for (i = 0; i < (RspFrm->operand[7] & 0x7f); i++) { + size <<= 8; + size += RspFrm->operand[8 + i]; + } +#endif + return RspFrm->operand[7]; +} + +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int pos; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag + CmdFrm.length = 12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + pos = get_ca_object_pos(&RspFrm); + app_info[0] = (TAG_APP_INFO >> 16) & 0xFF; + app_info[1] = (TAG_APP_INFO >> 8) & 0xFF; + app_info[2] = (TAG_APP_INFO >> 0) & 0xFF; + app_info[3] = 6 + RspFrm.operand[pos + 4]; + app_info[4] = 0x01; + memcpy(&app_info[5], &RspFrm.operand[pos], 5 + RspFrm.operand[pos + 4]); + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int pos; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag + CmdFrm.length = 12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + pos = get_ca_object_pos(&RspFrm); + app_info[0] = (TAG_CA_INFO >> 16) & 0xFF; + app_info[1] = (TAG_CA_INFO >> 8) & 0xFF; + app_info[2] = (TAG_CA_INFO >> 0) & 0xFF; + app_info[3] = 2; + app_info[4] = RspFrm.operand[pos + 0]; + app_info[5] = RspFrm.operand[pos + 1]; + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_reset(struct firedtv *fdtv) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_RESET; // ca tag + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 1; // length + CmdFrm.operand[8] = 0; // force hardware reset + CmdFrm.length = 12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + return 0; +} + +int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + int list_management; + int program_info_length; + int pmt_cmd_id; + int read_pos; + int write_pos; + int es_info_length; + int crc32_csum; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = CONTROL; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + if (msg[0] != LIST_MANAGEMENT_ONLY) { + dev_info(&fdtv->ud->device, + "forcing list_management to ONLY\n"); + msg[0] = LIST_MANAGEMENT_ONLY; + } + // We take the cmd_id from the programme level only! + list_management = msg[0]; + program_info_length = ((msg[4] & 0x0F) << 8) + msg[5]; + if (program_info_length > 0) + program_info_length--; // Remove pmt_cmd_id + pmt_cmd_id = msg[6]; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_PMT; // ca tag + CmdFrm.operand[6] = 0; // more/last + //CmdFrm.operand[7] = XXXprogram_info_length + 17; // length + CmdFrm.operand[8] = list_management; + CmdFrm.operand[9] = 0x01; // pmt_cmd=OK_descramble + + // TS program map table + + // Table id=2 + CmdFrm.operand[10] = 0x02; + // Section syntax + length + CmdFrm.operand[11] = 0x80; + //CmdFrm.operand[12] = XXXprogram_info_length + 12; + // Program number + CmdFrm.operand[13] = msg[1]; + CmdFrm.operand[14] = msg[2]; + // Version number=0 + current/next=1 + CmdFrm.operand[15] = 0x01; + // Section number=0 + CmdFrm.operand[16] = 0x00; + // Last section number=0 + CmdFrm.operand[17] = 0x00; + // PCR_PID=1FFF + CmdFrm.operand[18] = 0x1F; + CmdFrm.operand[19] = 0xFF; + // Program info length + CmdFrm.operand[20] = (program_info_length >> 8); + CmdFrm.operand[21] = (program_info_length & 0xFF); + // CA descriptors at programme level + read_pos = 6; + write_pos = 22; + if (program_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(&fdtv->ud->device, + "invalid pmt_cmd_id %d\n", pmt_cmd_id); + + memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], + program_info_length); + read_pos += program_info_length; + write_pos += program_info_length; + } + while (read_pos < length) { + CmdFrm.operand[write_pos++] = msg[read_pos++]; + CmdFrm.operand[write_pos++] = msg[read_pos++]; + CmdFrm.operand[write_pos++] = msg[read_pos++]; + es_info_length = + ((msg[read_pos] & 0x0F) << 8) + msg[read_pos + 1]; + read_pos += 2; + if (es_info_length > 0) + es_info_length--; // Remove pmt_cmd_id + CmdFrm.operand[write_pos++] = es_info_length >> 8; + CmdFrm.operand[write_pos++] = es_info_length & 0xFF; + if (es_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(&fdtv->ud->device, + "invalid pmt_cmd_id %d " + "at stream level\n", pmt_cmd_id); + + memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], + es_info_length); + read_pos += es_info_length; + write_pos += es_info_length; + } + } + + // CRC + CmdFrm.operand[write_pos++] = 0x00; + CmdFrm.operand[write_pos++] = 0x00; + CmdFrm.operand[write_pos++] = 0x00; + CmdFrm.operand[write_pos++] = 0x00; + + CmdFrm.operand[7] = write_pos - 8; + CmdFrm.operand[12] = write_pos - 13; + + crc32_csum = crc32_be(0, &CmdFrm.operand[10], + CmdFrm.operand[12] - 1); + CmdFrm.operand[write_pos - 4] = (crc32_csum >> 24) & 0xFF; + CmdFrm.operand[write_pos - 3] = (crc32_csum >> 16) & 0xFF; + CmdFrm.operand[write_pos - 2] = (crc32_csum >> 8) & 0xFF; + CmdFrm.operand[write_pos - 1] = (crc32_csum >> 0) & 0xFF; + + CmdFrm.length = ALIGN(3 + write_pos, 4); + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + if (RspFrm.resp != ACCEPTED) { + dev_err(&fdtv->ud->device, + "CA PMT failed with response 0x%x\n", RspFrm.resp); + return -EFAULT; + } + + return 0; +} + +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; // ca tag + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *interval = RspFrm.operand[get_ca_object_pos(&RspFrm)]; + + return 0; +} + +int avc_ca_enter_menu(struct firedtv *fdtv) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + return 0; +} + +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) +{ + AVCCmdFrm CmdFrm; + AVCRspFrm RspFrm; + + memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); + CmdFrm.cts = AVC; + CmdFrm.ctype = STATUS; + CmdFrm.sutyp = 0x5; + CmdFrm.suid = fdtv->subunit; + CmdFrm.opcode = VENDOR; + + CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; + CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; + CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; + CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; + CmdFrm.operand[4] = 0; // slot + CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_MMI; + CmdFrm.operand[6] = 0; // more/last + CmdFrm.operand[7] = 0; // length + CmdFrm.length = 12; + + if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *len = get_ca_object_length(&RspFrm); + memcpy(mmi_object, &RspFrm.operand[get_ca_object_pos(&RspFrm)], *len); + + return 0; +} diff --git a/drivers/media/dvb/firewire/avc.h b/drivers/media/dvb/firewire/avc.h new file mode 100644 index 000000000000..168f371dbde0 --- /dev/null +++ b/drivers/media/dvb/firewire/avc.h @@ -0,0 +1,432 @@ +/* + * AV/C API + * + * Copyright (C) 2000 Manfred Weihs + * Copyright (C) 2003 Philipp Gutgsell <0014guph@edu.fh-kaernten.ac.at> + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This is based on code written by Peter Halwachs, Thomas Groiss and + * Andreas Monitzer. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _AVC_API_H +#define _AVC_API_H + +#include <linux/types.h> + +/************************************************************* + Constants from EN510221 +**************************************************************/ +#define LIST_MANAGEMENT_ONLY 0x03 + +/************************************************************ + definition of structures +*************************************************************/ +typedef struct { + int Nr_SourcePlugs; + int Nr_DestinationPlugs; +} TunerInfo; + + +/*********************************************** + + supported cts + +************************************************/ + +#define AVC 0x0 + +// FCP command frame with ctype = 0x0 is AVC command frame + +#ifdef __LITTLE_ENDIAN + +// Definition FCP Command Frame +typedef struct _AVCCmdFrm +{ + // AV/C command frame + __u8 ctype : 4 ; // command type + __u8 cts : 4 ; // always 0x0 for AVC + __u8 suid : 3 ; // subunit ID + __u8 sutyp : 5 ; // subunit_typ + __u8 opcode : 8 ; // opcode + __u8 operand[509] ; // array of operands [1-507] + int length; //length of the command frame +} AVCCmdFrm ; + +// Definition FCP Response Frame +typedef struct _AVCRspFrm +{ + // AV/C response frame + __u8 resp : 4 ; // response type + __u8 cts : 4 ; // always 0x0 for AVC + __u8 suid : 3 ; // subunit ID + __u8 sutyp : 5 ; // subunit_typ + __u8 opcode : 8 ; // opcode + __u8 operand[509] ; // array of operands [1-507] + int length; //length of the response frame +} AVCRspFrm ; + +#else + +typedef struct _AVCCmdFrm +{ + __u8 cts:4; + __u8 ctype:4; + __u8 sutyp:5; + __u8 suid:3; + __u8 opcode; + __u8 operand[509]; + int length; +} AVCCmdFrm; + +typedef struct _AVCRspFrm +{ + __u8 cts:4; + __u8 resp:4; + __u8 sutyp:5; + __u8 suid:3; + __u8 opcode; + __u8 operand[509]; + int length; +} AVCRspFrm; + +#endif + +/************************************************************* + AVC command types (ctype) +**************************************************************/// +#define CONTROL 0x00 +#define STATUS 0x01 +#define INQUIRY 0x02 +#define NOTIFY 0x03 + +/************************************************************* + AVC respond types +**************************************************************/// +#define NOT_IMPLEMENTED 0x8 +#define ACCEPTED 0x9 +#define REJECTED 0xA +#define STABLE 0xC +#define CHANGED 0xD +#define INTERIM 0xF + +/************************************************************* + AVC opcodes +**************************************************************/// +#define CONNECT 0x24 +#define DISCONNECT 0x25 +#define UNIT_INFO 0x30 +#define SUBUNIT_Info 0x31 +#define VENDOR 0x00 + +#define PLUG_INFO 0x02 +#define OPEN_DESCRIPTOR 0x08 +#define READ_DESCRIPTOR 0x09 +#define OBJECT_NUMBER_SELECT 0x0D + +/************************************************************* + AVCTuner opcodes +**************************************************************/ + +#define DSIT 0xC8 +#define DSD 0xCB +#define DESCRIPTOR_TUNER_STATUS 0x80 +#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 + +/************************************************************* + AVCTuner list types +**************************************************************/ +#define Multiplex_List 0x80 +#define Service_List 0x82 + +/************************************************************* + AVCTuner object entries +**************************************************************/ +#define Multiplex 0x80 +#define Service 0x82 +#define Service_with_specified_components 0x83 +#define Preferred_components 0x90 +#define Component 0x84 + +/************************************************************* + Vendor-specific commands +**************************************************************/ + +// digital everywhere vendor ID +#define SFE_VENDOR_DE_COMPANYID_0 0x00 +#define SFE_VENDOR_DE_COMPANYID_1 0x12 +#define SFE_VENDOR_DE_COMPANYID_2 0x87 + +#define SFE_VENDOR_MAX_NR_COMPONENTS 0x4 +#define SFE_VENDOR_MAX_NR_SERVICES 0x3 +#define SFE_VENDOR_MAX_NR_DSD_ELEMENTS 0x10 + +// vendor commands +#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0A +#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 +#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 // QPSK command for DVB-S + +// TODO: following vendor specific commands needs to be implemented +#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 +#define SFE_VENDOR_OPCODE_HOST2CA 0x56 +#define SFE_VENDOR_OPCODE_CA2HOST 0x57 +#define SFE_VENDOR_OPCODE_CISTATUS 0x59 +#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 // QPSK command for DVB-S2 devices + +// CA Tags +#define SFE_VENDOR_TAG_CA_RESET 0x00 +#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 +#define SFE_VENDOR_TAG_CA_PMT 0x02 +#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 +#define SFE_VENDOR_TAG_CA_MMI 0x05 +#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 + + +//AVCTuner DVB identifier service_ID +#define DVB 0x20 + +/************************************************************* + AVC descriptor types +**************************************************************/ + +#define Subunit_Identifier_Descriptor 0x00 +#define Tuner_Status_Descriptor 0x80 + +typedef struct { + __u8 Subunit_Type; + __u8 Max_Subunit_ID; +} SUBUNIT_INFO; + +/************************************************************* + + AVCTuner DVB object IDs are 6 byte long + +**************************************************************/ + +typedef struct { + __u8 Byte0; + __u8 Byte1; + __u8 Byte2; + __u8 Byte3; + __u8 Byte4; + __u8 Byte5; +}OBJECT_ID; + +/************************************************************* + MULIPLEX Structs +**************************************************************/ +typedef struct +{ +#ifdef __LITTLE_ENDIAN + __u8 RF_frequency_hByte:6; + __u8 raster_Frequency:2;//Bit7,6 raster frequency +#else + __u8 raster_Frequency:2; + __u8 RF_frequency_hByte:6; +#endif + __u8 RF_frequency_mByte; + __u8 RF_frequency_lByte; + +}FREQUENCY; + +#ifdef __LITTLE_ENDIAN + +typedef struct +{ + __u8 Modulation :1; + __u8 FEC_inner :1; + __u8 FEC_outer :1; + __u8 Symbol_Rate :1; + __u8 Frequency :1; + __u8 Orbital_Pos :1; + __u8 Polarisation :1; + __u8 reserved_fields :1; + __u8 reserved1 :7; + __u8 Network_ID :1; + +}MULTIPLEX_VALID_FLAGS; + +typedef struct +{ + __u8 GuardInterval:1; + __u8 CodeRateLPStream:1; + __u8 CodeRateHPStream:1; + __u8 HierarchyInfo:1; + __u8 Constellation:1; + __u8 Bandwidth:1; + __u8 CenterFrequency:1; + __u8 reserved1:1; + __u8 reserved2:5; + __u8 OtherFrequencyFlag:1; + __u8 TransmissionMode:1; + __u8 NetworkId:1; +}MULTIPLEX_VALID_FLAGS_DVBT; + +#else + +typedef struct { + __u8 reserved_fields:1; + __u8 Polarisation:1; + __u8 Orbital_Pos:1; + __u8 Frequency:1; + __u8 Symbol_Rate:1; + __u8 FEC_outer:1; + __u8 FEC_inner:1; + __u8 Modulation:1; + __u8 Network_ID:1; + __u8 reserved1:7; +}MULTIPLEX_VALID_FLAGS; + +typedef struct { + __u8 reserved1:1; + __u8 CenterFrequency:1; + __u8 Bandwidth:1; + __u8 Constellation:1; + __u8 HierarchyInfo:1; + __u8 CodeRateHPStream:1; + __u8 CodeRateLPStream:1; + __u8 GuardInterval:1; + __u8 NetworkId:1; + __u8 TransmissionMode:1; + __u8 OtherFrequencyFlag:1; + __u8 reserved2:5; +}MULTIPLEX_VALID_FLAGS_DVBT; + +#endif + +typedef union { + MULTIPLEX_VALID_FLAGS Bits; + MULTIPLEX_VALID_FLAGS_DVBT Bits_T; + struct { + __u8 ByteHi; + __u8 ByteLo; + } Valid_Word; +} M_VALID_FLAGS; + +typedef struct +{ +#ifdef __LITTLE_ENDIAN + __u8 ActiveSystem; + __u8 reserved:5; + __u8 NoRF:1; + __u8 Moving:1; + __u8 Searching:1; + + __u8 SelectedAntenna:7; + __u8 Input:1; + + __u8 BER[4]; + + __u8 SignalStrength; + FREQUENCY Frequency; + + __u8 ManDepInfoLength; + + __u8 PowerSupply:1; + __u8 FrontEndPowerStatus:1; + __u8 reserved3:1; + __u8 AntennaError:1; + __u8 FrontEndError:1; + __u8 reserved2:3; + + __u8 CarrierNoiseRatio[2]; + __u8 reserved4[2]; + __u8 PowerSupplyVoltage; + __u8 AntennaVoltage; + __u8 FirewireBusVoltage; + + __u8 CaMmi:1; + __u8 reserved5:7; + + __u8 reserved6:1; + __u8 CaInitializationStatus:1; + __u8 CaErrorFlag:1; + __u8 CaDvbFlag:1; + __u8 CaModulePresentStatus:1; + __u8 CaApplicationInfo:1; + __u8 CaDateTimeRequest:1; + __u8 CaPmtReply:1; + +#else + __u8 ActiveSystem; + __u8 Searching:1; + __u8 Moving:1; + __u8 NoRF:1; + __u8 reserved:5; + + __u8 Input:1; + __u8 SelectedAntenna:7; + + __u8 BER[4]; + + __u8 SignalStrength; + FREQUENCY Frequency; + + __u8 ManDepInfoLength; + + __u8 reserved2:3; + __u8 FrontEndError:1; + __u8 AntennaError:1; + __u8 reserved3:1; + __u8 FrontEndPowerStatus:1; + __u8 PowerSupply:1; + + __u8 CarrierNoiseRatio[2]; + __u8 reserved4[2]; + __u8 PowerSupplyVoltage; + __u8 AntennaVoltage; + __u8 FirewireBusVoltage; + + __u8 reserved5:7; + __u8 CaMmi:1; + __u8 CaPmtReply:1; + __u8 CaDateTimeRequest:1; + __u8 CaApplicationInfo:1; + __u8 CaModulePresentStatus:1; + __u8 CaDvbFlag:1; + __u8 CaErrorFlag:1; + __u8 CaInitializationStatus:1; + __u8 reserved6:1; + +#endif +} ANTENNA_INPUT_INFO; // 22 Byte + +#define LNBCONTROL_DONTCARE 0xff + +struct dvb_diseqc_master_cmd; +struct dvb_frontend_parameters; +struct firedtv; + +int avc_recv(struct firedtv *fdtv, u8 *data, size_t length); + +int AVCTuner_DSIT(struct firedtv *fdtv, int Source_Plug, + struct dvb_frontend_parameters *params, __u8 *status); + +int avc_tuner_status(struct firedtv *fdtv, + ANTENNA_INPUT_INFO *antenna_input_info); +int avc_tuner_dsd(struct firedtv *fdtv, + struct dvb_frontend_parameters *params); +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]); +int avc_tuner_get_ts(struct firedtv *fdtv); +int avc_identify_subunit(struct firedtv *fdtv); +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd); +void avc_remote_ctrl_work(struct work_struct *work); +int avc_register_remote_control(struct firedtv *fdtv); +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_reset(struct firedtv *fdtv); +int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length); +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval); +int avc_ca_enter_menu(struct firedtv *fdtv); +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len); + +#endif /* _AVC_API_H */ diff --git a/drivers/media/dvb/firewire/cmp.c b/drivers/media/dvb/firewire/cmp.c new file mode 100644 index 000000000000..821e033d8195 --- /dev/null +++ b/drivers/media/dvb/firewire/cmp.c @@ -0,0 +1,171 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/types.h> + +#include <asm/byteorder.h> + +#include <ieee1394.h> +#include <nodemgr.h> + +#include "avc.h" +#include "cmp.h" +#include "firedtv.h" + +#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL + +static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = hpsb_node_read(fdtv->ud->ne, addr, buf, len); + if (ret < 0) + dev_err(&fdtv->ud->device, "CMP: read I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg, + int ext_tcode) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = hpsb_node_lock(fdtv->ud->ne, addr, ext_tcode, data, + (__force quadlet_t)arg); + if (ret < 0) + dev_err(&fdtv->ud->device, "CMP: lock I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) +{ + return (be32_to_cpu(opcr) >> shift) & mask; +} + +static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) +{ + *opcr &= ~cpu_to_be32(mask << shift); + *opcr |= cpu_to_be32((value & mask) << shift); +} + +#define get_opcr_online(v) get_opcr((v), 0x1, 31) +#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) +#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) + +#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) +#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) +#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) +#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) + +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + int ret; + + ret = cmp_read(fdtv, &opcr, opcr_address, 4); + if (ret < 0) + return ret; + +repeat: + if (!get_opcr_online(opcr)) { + dev_err(&fdtv->ud->device, "CMP: output offline\n"); + return -EBUSY; + } + + old_opcr = opcr; + + if (get_opcr_p2p_connections(opcr)) { + if (get_opcr_channel(opcr) != channel) { + dev_err(&fdtv->ud->device, + "CMP: cannot change channel\n"); + return -EBUSY; + } + dev_info(&fdtv->ud->device, + "CMP: overlaying existing connection\n"); + + /* We don't allocate isochronous resources. */ + } else { + set_opcr_channel(&opcr, channel); + set_opcr_data_rate(&opcr, IEEE1394_SPEED_400); + + /* FIXME: this is for the worst case - optimize */ + set_opcr_overhead_id(&opcr, 0); + + /* FIXME: allocate isochronous channel and bandwidth at IRM */ + } + + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); + + ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr, 2); + if (ret < 0) + return ret; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections > 0, + * deallocate isochronous channel and bandwidth at IRM + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + return -EBUSY; + } + + return 0; +} + +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + + if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0) + return; + +repeat: + if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || + get_opcr_channel(opcr) != channel) { + dev_err(&fdtv->ud->device, "CMP: no connection to break\n"); + return; + } + + old_opcr = opcr; + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); + + if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr, 2) < 0) + return; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last + * owner, deallocate isochronous channel and bandwidth at IRM + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + } +} diff --git a/drivers/media/dvb/firewire/cmp.h b/drivers/media/dvb/firewire/cmp.h new file mode 100644 index 000000000000..17e182cf29a9 --- /dev/null +++ b/drivers/media/dvb/firewire/cmp.h @@ -0,0 +1,9 @@ +#ifndef _CMP_H +#define _CMP_H + +struct firedtv; + +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel); +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel); + +#endif /* _CMP_H */ diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c new file mode 100644 index 000000000000..953618246e8e --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -0,0 +1,291 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> +#include <dvbdev.h> + +#include <csr1212.h> +#include <highlevel.h> +#include <hosts.h> +#include <ieee1394_hotplug.h> +#include <nodemgr.h> + +#include "avc.h" +#include "cmp.h" +#include "firedtv.h" +#include "firedtv-ci.h" +#include "firedtv-rc.h" + +#define MATCH_FLAGS IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ + IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION +#define DIGITAL_EVERYWHERE_OUI 0x001287 + +static struct ieee1394_device_id fdtv_id_table[] = { + + { + /* FloppyDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000024, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + },{ + /* FloppyDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000025, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + },{ + /* FloppyDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000026, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + },{ + /* FireDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000034, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + },{ + /* FireDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000035, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + },{ + /* FireDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000036, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { } +}; + +MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); + +/* list of all firedtv devices */ +LIST_HEAD(fdtv_list); +DEFINE_SPINLOCK(fdtv_list_lock); + +static void fcp_request(struct hpsb_host *host, + int nodeid, + int direction, + int cts, + u8 *data, + size_t length) +{ + struct firedtv *fdtv = NULL; + struct firedtv *fdtv_entry; + unsigned long flags; + + if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { + + spin_lock_irqsave(&fdtv_list_lock, flags); + list_for_each_entry(fdtv_entry,&fdtv_list,list) { + if (fdtv_entry->ud->ne->host == host && + fdtv_entry->ud->ne->nodeid == nodeid && + (fdtv_entry->subunit == (data[1]&0x7) || + (fdtv_entry->subunit == 0 && + (data[1]&0x7) == 0x7))) { + fdtv=fdtv_entry; + break; + } + } + spin_unlock_irqrestore(&fdtv_list_lock, flags); + + if (fdtv) + avc_recv(fdtv, data, length); + } +} + +const char *fdtv_model_names[] = { + [FIREDTV_UNKNOWN] = "unknown type", + [FIREDTV_DVB_S] = "FireDTV S/CI", + [FIREDTV_DVB_C] = "FireDTV C/CI", + [FIREDTV_DVB_T] = "FireDTV T/CI", + [FIREDTV_DVB_S2] = "FireDTV S2 ", +}; + +static int fdtv_probe(struct device *dev) +{ + struct unit_directory *ud = + container_of(dev, struct unit_directory, device); + struct firedtv *fdtv; + unsigned long flags; + int kv_len; + void *kv_str; + int i; + int err = -ENOMEM; + + fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); + if (!fdtv) + return -ENOMEM; + + dev->driver_data = fdtv; + fdtv->ud = ud; + fdtv->subunit = 0; + fdtv->isochannel = -1; + fdtv->tone = 0xff; + fdtv->voltage = 0xff; + + mutex_init(&fdtv->avc_mutex); + init_waitqueue_head(&fdtv->avc_wait); + fdtv->avc_reply_received = true; + mutex_init(&fdtv->demux_mutex); + INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); + + /* Reading device model from ROM */ + kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); + kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); + for (i = ARRAY_SIZE(fdtv_model_names); --i;) + if (strlen(fdtv_model_names[i]) <= kv_len && + strncmp(kv_str, fdtv_model_names[i], kv_len) == 0) + break; + fdtv->type = i; + + /* + * Work around a bug in udev's path_id script: Use the fw-host's dev + * instead of the unit directory's dev as parent of the input device. + */ + err = fdtv_register_rc(fdtv, dev->parent->parent); + if (err) + goto fail_free; + + INIT_LIST_HEAD(&fdtv->list); + spin_lock_irqsave(&fdtv_list_lock, flags); + list_add_tail(&fdtv->list, &fdtv_list); + spin_unlock_irqrestore(&fdtv_list_lock, flags); + + err = avc_identify_subunit(fdtv); + if (err) + goto fail; + + err = fdtv_dvbdev_init(fdtv, dev); + if (err) + goto fail; + + avc_register_remote_control(fdtv); + return 0; + +fail: + spin_lock_irqsave(&fdtv_list_lock, flags); + list_del(&fdtv->list); + spin_unlock_irqrestore(&fdtv_list_lock, flags); + fdtv_unregister_rc(fdtv); +fail_free: + kfree(fdtv); + return err; +} + +static int fdtv_remove(struct device *dev) +{ + struct firedtv *fdtv = dev->driver_data; + unsigned long flags; + + fdtv_ca_release(fdtv); + dvb_unregister_frontend(&fdtv->fe); + dvb_net_release(&fdtv->dvbnet); + fdtv->demux.dmx.close(&fdtv->demux.dmx); + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, + &fdtv->frontend); + dvb_dmxdev_release(&fdtv->dmxdev); + dvb_dmx_release(&fdtv->demux); + dvb_unregister_adapter(&fdtv->adapter); + + spin_lock_irqsave(&fdtv_list_lock, flags); + list_del(&fdtv->list); + spin_unlock_irqrestore(&fdtv_list_lock, flags); + + cancel_work_sync(&fdtv->remote_ctrl_work); + fdtv_unregister_rc(fdtv); + + kfree(fdtv); + return 0; +} + +static int fdtv_update(struct unit_directory *ud) +{ + struct firedtv *fdtv = ud->device.driver_data; + + if (fdtv->isochannel >= 0) + cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); + return 0; +} + +static struct hpsb_protocol_driver fdtv_driver = { + + .name = "firedtv", + .id_table = fdtv_id_table, + .update = fdtv_update, + + .driver = { + //.name and .bus are filled in for us in more recent linux versions + //.name = "FireDTV", + //.bus = &ieee1394_bus_type, + .probe = fdtv_probe, + .remove = fdtv_remove, + }, +}; + +static struct hpsb_highlevel fdtv_highlevel = { + .name = "firedtv", + .fcp_request = fcp_request, +}; + +static int __init fdtv_init(void) +{ + int ret; + + hpsb_register_highlevel(&fdtv_highlevel); + ret = hpsb_register_protocol(&fdtv_driver); + if (ret) { + printk(KERN_ERR "firedtv: failed to register protocol\n"); + hpsb_unregister_highlevel(&fdtv_highlevel); + } + return ret; +} + +static void __exit fdtv_exit(void) +{ + hpsb_unregister_protocol(&fdtv_driver); + hpsb_unregister_highlevel(&fdtv_highlevel); +} + +module_init(fdtv_init); +module_exit(fdtv_exit); + +MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); +MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); +MODULE_DESCRIPTION("FireDTV DVB Driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/dvb/firewire/firedtv-ci.c new file mode 100644 index 000000000000..6d87926b8bfe --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-ci.c @@ -0,0 +1,261 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/dvb/ca.h> +#include <linux/fs.h> +#include <linux/module.h> + +#include <dvbdev.h> + +#include "avc.h" +#include "firedtv.h" +#include "firedtv-ci.h" + +static int fdtv_ca_ready(ANTENNA_INPUT_INFO *info) +{ + return info->CaInitializationStatus == 1 && + info->CaErrorFlag == 0 && + info->CaDvbFlag == 1 && + info->CaModulePresentStatus == 1; +} + +static int fdtv_get_ca_flags(ANTENNA_INPUT_INFO *info) +{ + int flags = 0; + + if (info->CaModulePresentStatus == 1) + flags |= CA_CI_MODULE_PRESENT; + if (info->CaInitializationStatus == 1 && + info->CaErrorFlag == 0 && + info->CaDvbFlag == 1) + flags |= CA_CI_MODULE_READY; + return flags; +} + +static int fdtv_ca_reset(struct firedtv *fdtv) +{ + return avc_ca_reset(fdtv) ? -EFAULT : 0; +} + +static int fdtv_ca_get_caps(void *arg) +{ + struct ca_caps *cap = arg; + + cap->slot_num = 1; + cap->slot_type = CA_CI; + cap->descr_num = 1; + cap->descr_type = CA_ECD; + return 0; +} + +static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) +{ + ANTENNA_INPUT_INFO info; + struct ca_slot_info *slot = arg; + + if (avc_tuner_status(fdtv, &info)) + return -EFAULT; + + if (slot->num != 0) + return -EFAULT; + + slot->type = CA_CI; + slot->flags = fdtv_get_ca_flags(&info); + return 0; +} + +static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return + avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_info(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return avc_ca_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return + avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) +{ + ANTENNA_INPUT_INFO info; + int err; + + switch (fdtv->ca_last_command) { + case TAG_APP_INFO_ENQUIRY: + err = fdtv_ca_app_info(fdtv, arg); + break; + case TAG_CA_INFO_ENQUIRY: + err = fdtv_ca_info(fdtv, arg); + break; + default: + if (avc_tuner_status(fdtv, &info)) + err = -EFAULT; + else if (info.CaMmi == 1) + err = fdtv_ca_get_mmi(fdtv, arg); + else { + printk(KERN_INFO "%s: Unhandled message 0x%08X\n", + __func__, fdtv->ca_last_command); + err = -EFAULT; + } + } + fdtv->ca_last_command = 0; + return err; +} + +static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *msg = arg; + int data_pos; + int data_length; + int i; + + data_pos = 4; + if (msg->msg[3] & 0x80) { + data_length = 0; + for (i = 0; i < (msg->msg[3] & 0x7F); i++) + data_length = (data_length << 8) + msg->msg[data_pos++]; + } else { + data_length = msg->msg[3]; + } + + return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? + -EFAULT : 0; +} + +static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *msg = arg; + int err; + + /* Do we need a semaphore for this? */ + fdtv->ca_last_command = + (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; + switch (fdtv->ca_last_command) { + case TAG_CA_PMT: + err = fdtv_ca_pmt(fdtv, arg); + break; + case TAG_APP_INFO_ENQUIRY: + /* handled in ca_get_msg */ + err = 0; + break; + case TAG_CA_INFO_ENQUIRY: + /* handled in ca_get_msg */ + err = 0; + break; + case TAG_ENTER_MENU: + err = avc_ca_enter_menu(fdtv); + break; + default: + printk(KERN_ERR "%s: Unhandled unknown message 0x%08X\n", + __func__, fdtv->ca_last_command); + err = -EFAULT; + } + return err; +} + +static int fdtv_ca_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct dvb_device *dvbdev = file->private_data; + struct firedtv *fdtv = dvbdev->priv; + ANTENNA_INPUT_INFO info; + int err; + + switch(cmd) { + case CA_RESET: + err = fdtv_ca_reset(fdtv); + break; + case CA_GET_CAP: + err = fdtv_ca_get_caps(arg); + break; + case CA_GET_SLOT_INFO: + err = fdtv_ca_get_slot_info(fdtv, arg); + break; + case CA_GET_MSG: + err = fdtv_ca_get_msg(fdtv, arg); + break; + case CA_SEND_MSG: + err = fdtv_ca_send_msg(fdtv, arg); + break; + default: + printk(KERN_INFO "%s: Unhandled ioctl, command: %u\n",__func__, + cmd); + err = -EOPNOTSUPP; + } + + /* FIXME Is this necessary? */ + avc_tuner_status(fdtv, &info); + + return err; +} + +static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) +{ + return POLLIN; +} + +static struct file_operations fdtv_ca_fops = { + .owner = THIS_MODULE, + .ioctl = dvb_generic_ioctl, + .open = dvb_generic_open, + .release = dvb_generic_release, + .poll = fdtv_ca_io_poll, +}; + +static struct dvb_device fdtv_ca = { + .users = 1, + .readers = 1, + .writers = 1, + .fops = &fdtv_ca_fops, + .kernel_ioctl = fdtv_ca_ioctl, +}; + +int fdtv_ca_register(struct firedtv *fdtv) +{ + ANTENNA_INPUT_INFO info; + int err; + + if (avc_tuner_status(fdtv, &info)) + return -EINVAL; + + if (!fdtv_ca_ready(&info)) + return -EFAULT; + + err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, + &fdtv_ca, fdtv, DVB_DEVICE_CA); + + if (info.CaApplicationInfo == 0) + printk(KERN_ERR "%s: CaApplicationInfo is not set.\n", + __func__); + if (info.CaDateTimeRequest == 1) + avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); + + return err; +} + +void fdtv_ca_release(struct firedtv *fdtv) +{ + if (fdtv->cadev) + dvb_unregister_device(fdtv->cadev); +} diff --git a/drivers/media/dvb/firewire/firedtv-ci.h b/drivers/media/dvb/firewire/firedtv-ci.h new file mode 100644 index 000000000000..d6840f5dcbae --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-ci.h @@ -0,0 +1,9 @@ +#ifndef _FIREDTV_CI_H +#define _FIREDTV_CI_H + +struct firedtv; + +int fdtv_ca_register(struct firedtv *fdtv); +void fdtv_ca_release(struct firedtv *fdtv); + +#endif /* _FIREDTV_CI_H */ diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c new file mode 100644 index 000000000000..1823058696f2 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -0,0 +1,276 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/types.h> + +#include <dvb_demux.h> +#include <dvb_frontend.h> +#include <dvbdev.h> + +#include "avc.h" +#include "firedtv.h" +#include "firedtv-ci.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct firedtv_channel *fdtv_channel_allocate(struct firedtv *fdtv) +{ + struct firedtv_channel *c = NULL; + int k; + + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return NULL; + + for (k = 0; k < 16; k++) + if (!fdtv->channel[k].active) { + fdtv->channel[k].active = true; + c = &fdtv->channel[k]; + break; + } + + mutex_unlock(&fdtv->demux_mutex); + return c; +} + +static int fdtv_channel_collect(struct firedtv *fdtv, int *pidc, u16 pid[]) +{ + int k, l = 0; + + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + + for (k = 0; k < 16; k++) + if (fdtv->channel[k].active) + pid[l++] = fdtv->channel[k].pid; + + mutex_unlock(&fdtv->demux_mutex); + + *pidc = l; + + return 0; +} + +static int fdtv_channel_release(struct firedtv *fdtv, + struct firedtv_channel *channel) +{ + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + + channel->active = false; + + mutex_unlock(&fdtv->demux_mutex); + return 0; +} + +int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct firedtv *fdtv = (struct firedtv*)dvbdmxfeed->demux->priv; + struct firedtv_channel *channel; + int pidc,k; + u16 pids[16]; + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + case DMX_TYPE_SEC: + break; + default: + printk(KERN_ERR "%s: invalid type %u\n", + __func__, dvbdmxfeed->type); + return -EINVAL; + } + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + switch (dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + case DMX_TS_PES_AUDIO: + case DMX_TS_PES_TELETEXT: + case DMX_TS_PES_PCR: + case DMX_TS_PES_OTHER: + //Dirty fix to keep fdtv->channel pid-list up to date + for(k=0;k<16;k++){ + if (!fdtv->channel[k].active) + fdtv->channel[k].pid = + dvbdmxfeed->pid; + break; + } + channel = fdtv_channel_allocate(fdtv); + break; + default: + printk(KERN_ERR "%s: invalid pes type %u\n", + __func__, dvbdmxfeed->pes_type); + return -EINVAL; + } + } else { + channel = fdtv_channel_allocate(fdtv); + } + + if (!channel) { + printk(KERN_ERR "%s: busy!\n", __func__); + return -EBUSY; + } + + dvbdmxfeed->priv = channel; + channel->pid = dvbdmxfeed->pid; + + if (fdtv_channel_collect(fdtv, &pidc, pids)) { + fdtv_channel_release(fdtv, channel); + printk(KERN_ERR "%s: could not collect pids!\n", __func__); + return -EINTR; + } + + if (dvbdmxfeed->pid == 8192) { + k = avc_tuner_get_ts(fdtv); + if (k) { + fdtv_channel_release(fdtv, channel); + printk("%s: AVCTuner_GetTS failed with error %d\n", + __func__, k); + return k; + } + } else { + k = avc_tuner_set_pids(fdtv, pidc, pids); + if (k) { + fdtv_channel_release(fdtv, channel); + printk("%s: AVCTuner_SetPIDs failed with error %d\n", + __func__, k); + return k; + } + } + + return 0; +} + +int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *demux = dvbdmxfeed->demux; + struct firedtv *fdtv = (struct firedtv*)demux->priv; + struct firedtv_channel *c = dvbdmxfeed->priv; + int k, l; + u16 pids[16]; + + if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE))) { + + if (dvbdmxfeed->ts_type & TS_DECODER) { + + if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || + !demux->pesfilter[dvbdmxfeed->pes_type]) + + return -EINVAL; + + demux->pids[dvbdmxfeed->pes_type] |= 0x8000; + demux->pesfilter[dvbdmxfeed->pes_type] = NULL; + } + + if (!(dvbdmxfeed->ts_type & TS_DECODER && + dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) + + return 0; + } + + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + + /* list except channel to be removed */ + for (k = 0, l = 0; k < 16; k++) + if (fdtv->channel[k].active) { + if (&fdtv->channel[k] != c) + pids[l++] = fdtv->channel[k].pid; + else + fdtv->channel[k].active = false; + } + + k = avc_tuner_set_pids(fdtv, l, pids); + if (!k) + c->active = false; + + mutex_unlock(&fdtv->demux_mutex); + return k; +} + +int fdtv_dvbdev_init(struct firedtv *fdtv, struct device *dev) +{ + int err; + + err = DVB_REGISTER_ADAPTER(&fdtv->adapter, + fdtv_model_names[fdtv->type], + THIS_MODULE, dev, adapter_nr); + if (err < 0) + goto fail_log; + + /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ + fdtv->demux.dmx.capabilities = 0; + + fdtv->demux.priv = fdtv; + fdtv->demux.filternum = 16; + fdtv->demux.feednum = 16; + fdtv->demux.start_feed = fdtv_start_feed; + fdtv->demux.stop_feed = fdtv_stop_feed; + fdtv->demux.write_to_decoder = NULL; + + err = dvb_dmx_init(&fdtv->demux); + if (err) + goto fail_unreg_adapter; + + fdtv->dmxdev.filternum = 16; + fdtv->dmxdev.demux = &fdtv->demux.dmx; + fdtv->dmxdev.capabilities = 0; + + err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); + if (err) + goto fail_dmx_release; + + fdtv->frontend.source = DMX_FRONTEND_0; + + err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, + &fdtv->frontend); + if (err) + goto fail_dmxdev_release; + + err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, + &fdtv->frontend); + if (err) + goto fail_rem_frontend; + + dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); + + fdtv_frontend_init(fdtv); + err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); + if (err) + goto fail_net_release; + + err = fdtv_ca_register(fdtv); + if (err) + dev_info(dev, "Conditional Access Module not enabled\n"); + + return 0; + +fail_net_release: + dvb_net_release(&fdtv->dvbnet); + fdtv->demux.dmx.close(&fdtv->demux.dmx); +fail_rem_frontend: + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, + &fdtv->frontend); +fail_dmxdev_release: + dvb_dmxdev_release(&fdtv->dmxdev); +fail_dmx_release: + dvb_dmx_release(&fdtv->demux); +fail_unreg_adapter: + dvb_unregister_adapter(&fdtv->adapter); +fail_log: + dev_err(dev, "DVB initialization failed\n"); + return err; +} + + diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c new file mode 100644 index 000000000000..f8150f402bb6 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-fe.c @@ -0,0 +1,245 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <dvb_frontend.h> + +#include "avc.h" +#include "cmp.h" +#include "firedtv.h" + +static int fdtv_dvb_init(struct dvb_frontend *fe) +{ + struct firedtv *fdtv = fe->sec_priv; + int err; + + /* FIXME - allocate free channel at IRM */ + fdtv->isochannel = fdtv->adapter.num; + + err = cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); + if (err) { + printk(KERN_ERR "Could not establish point to point " + "connection.\n"); + return err; + } + + return setup_iso_channel(fdtv); +} + +static int fdtv_sleep(struct dvb_frontend *fe) +{ + struct firedtv *fdtv = fe->sec_priv; + + tear_down_iso_channel(fdtv); + cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); + fdtv->isochannel = -1; + return 0; +} + +static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct firedtv *fdtv = fe->sec_priv; + + return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, + LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, 1, cmd); +} + +static int fdtv_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t minicmd) +{ + return 0; +} + +static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->tone = tone; + return 0; +} + +static int fdtv_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->voltage = voltage; + return 0; +} + +static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct firedtv *fdtv = fe->sec_priv; + ANTENNA_INPUT_INFO info; + + if (avc_tuner_status(fdtv, &info)) + return -EINVAL; + + if (info.NoRF) + *status = 0; + else + *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; +} + +static int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct firedtv *fdtv = fe->sec_priv; + ANTENNA_INPUT_INFO info; + + if (avc_tuner_status(fdtv, &info)) + return -EINVAL; + + *ber = info.BER[0] << 24 | info.BER[1] << 16 | + info.BER[2] << 8 | info.BER[3]; + return 0; +} + +static int fdtv_read_signal_strength (struct dvb_frontend *fe, u16 *strength) +{ + struct firedtv *fdtv = fe->sec_priv; + ANTENNA_INPUT_INFO info; + + if (avc_tuner_status(fdtv, &info)) + return -EINVAL; + + *strength = info.SignalStrength << 8; + return 0; +} + +static int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct firedtv *fdtv = fe->sec_priv; + ANTENNA_INPUT_INFO info; + + if (avc_tuner_status(fdtv, &info)) + return -EINVAL; + + /* C/N[dB] = -10 * log10(snr / 65535) */ + *snr = (info.CarrierNoiseRatio[0] << 8) + info.CarrierNoiseRatio[1]; + *snr *= 257; + return 0; +} + +static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + return -EOPNOTSUPP; +} + +static int fdtv_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct firedtv *fdtv = fe->sec_priv; + + /* FIXME: avc_tuner_dsd never returns ACCEPTED. Check status? */ + if (avc_tuner_dsd(fdtv, params) != ACCEPTED) + return -EINVAL; + else + return 0; /* not sure of this... */ +} + +static int fdtv_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + return -EOPNOTSUPP; +} + +void fdtv_frontend_init(struct firedtv *fdtv) +{ + struct dvb_frontend_ops *ops = &fdtv->fe.ops; + struct dvb_frontend_info *fi = &ops->info; + + ops->init = fdtv_dvb_init; + ops->sleep = fdtv_sleep; + + ops->set_frontend = fdtv_set_frontend; + ops->get_frontend = fdtv_get_frontend; + + ops->read_status = fdtv_read_status; + ops->read_ber = fdtv_read_ber; + ops->read_signal_strength = fdtv_read_signal_strength; + ops->read_snr = fdtv_read_snr; + ops->read_ucblocks = fdtv_read_uncorrected_blocks; + + ops->diseqc_send_master_cmd = fdtv_diseqc_send_master_cmd; + ops->diseqc_send_burst = fdtv_diseqc_send_burst; + ops->set_tone = fdtv_set_tone; + ops->set_voltage = fdtv_set_voltage; + + switch (fdtv->type) { + case FIREDTV_DVB_S: + fi->type = FE_QPSK; + + fi->frequency_min = 950000; + fi->frequency_max = 2150000; + fi->frequency_stepsize = 125; + fi->symbol_rate_min = 1000000; + fi->symbol_rate_max = 40000000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK; + break; + + case FIREDTV_DVB_C: + fi->type = FE_QAM; + + fi->frequency_min = 47000000; + fi->frequency_max = 866000000; + fi->frequency_stepsize = 62500; + fi->symbol_rate_min = 870000; + fi->symbol_rate_max = 6900000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO; + break; + + case FIREDTV_DVB_T: + fi->type = FE_OFDM; + + fi->frequency_min = 49000000; + fi->frequency_max = 861000000; + fi->frequency_stepsize = 62500; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_2_3 | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO; + break; + + default: + printk(KERN_ERR "FireDTV: no frontend for model type %d\n", + fdtv->type); + } + strcpy(fi->name, fdtv_model_names[fdtv->type]); + + fdtv->fe.dvb = &fdtv->adapter; + fdtv->fe.sec_priv = fdtv; +} diff --git a/drivers/media/dvb/firewire/firedtv-iso.c b/drivers/media/dvb/firewire/firedtv-iso.c new file mode 100644 index 000000000000..a72df228e7de --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-iso.c @@ -0,0 +1,111 @@ +/* + * FireSAT DVB driver + * + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/spinlock.h> + +#include <dvb_demux.h> + +#include <dma.h> +#include <iso.h> +#include <nodemgr.h> + +#include "firedtv.h" + +static void rawiso_activity_cb(struct hpsb_iso *iso); + +void tear_down_iso_channel(struct firedtv *fdtv) +{ + if (fdtv->iso_handle != NULL) { + hpsb_iso_stop(fdtv->iso_handle); + hpsb_iso_shutdown(fdtv->iso_handle); + } + fdtv->iso_handle = NULL; +} + +int setup_iso_channel(struct firedtv *fdtv) +{ + int result; + fdtv->iso_handle = + hpsb_iso_recv_init(fdtv->ud->ne->host, + 256 * 200, //data_buf_size, + 256, //buf_packets, + fdtv->isochannel, + HPSB_ISO_DMA_DEFAULT, //dma_mode, + -1, //stat.config.irq_interval, + rawiso_activity_cb); + if (fdtv->iso_handle == NULL) { + printk(KERN_ERR "Cannot initialize iso receive.\n"); + return -EINVAL; + } + result = hpsb_iso_recv_start(fdtv->iso_handle, -1, -1, 0); + if (result != 0) { + printk(KERN_ERR "Cannot start iso receive.\n"); + return -EINVAL; + } + return 0; +} + +static void rawiso_activity_cb(struct hpsb_iso *iso) +{ + unsigned int num; + unsigned int i; + unsigned int packet; + unsigned long flags; + struct firedtv *fdtv = NULL; + struct firedtv *fdtv_iterator; + + spin_lock_irqsave(&fdtv_list_lock, flags); + list_for_each_entry(fdtv_iterator, &fdtv_list, list) { + if(fdtv_iterator->iso_handle == iso) { + fdtv = fdtv_iterator; + break; + } + } + spin_unlock_irqrestore(&fdtv_list_lock, flags); + + if (fdtv) { + packet = iso->first_packet; + num = hpsb_iso_n_ready(iso); + for (i = 0; i < num; i++, + packet = (packet + 1) % iso->buf_packets) { + unsigned char *buf = + dma_region_i(&iso->data_buf, unsigned char, + iso->infos[packet].offset + + sizeof(struct CIPHeader)); + int count = (iso->infos[packet].len - + sizeof(struct CIPHeader)) / + (188 + sizeof(struct firewireheader)); + if (iso->infos[packet].len <= sizeof(struct CIPHeader)) + continue; // ignore empty packet + + while (count --) { + if (buf[sizeof(struct firewireheader)] == 0x47) + dvb_dmx_swfilter_packets(&fdtv->demux, + &buf[sizeof(struct firewireheader)], 1); + else + printk("%s: invalid packet, skipping\n", __func__); + buf += 188 + sizeof(struct firewireheader); + + } + + } + hpsb_iso_recv_release_packets(iso, num); + } + else { + printk("%s: packets for unknown iso channel, skipping\n", + __func__); + hpsb_iso_recv_release_packets(iso, hpsb_iso_n_ready(iso)); + } +} + diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c new file mode 100644 index 000000000000..436c0c69a13d --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-rc.c @@ -0,0 +1,191 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/bitops.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "firedtv-rc.h" +#include "firedtv.h" + +/* fixed table with older keycodes, geared towards MythTV */ +const static u16 oldtable[] = { + + /* code from device: 0x4501...0x451f */ + + KEY_ESC, + KEY_F9, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_I, + KEY_0, + KEY_ENTER, + KEY_RED, + KEY_UP, + KEY_GREEN, + KEY_F10, + KEY_SPACE, + KEY_F11, + KEY_YELLOW, + KEY_DOWN, + KEY_BLUE, + KEY_Z, + KEY_P, + KEY_PAGEDOWN, + KEY_LEFT, + KEY_W, + KEY_RIGHT, + KEY_P, + KEY_M, + + /* code from device: 0x4540...0x4542 */ + + KEY_R, + KEY_V, + KEY_C, +}; + +/* user-modifiable table for a remote as sold in 2008 */ +const static u16 keytable[] = { + + /* code from device: 0x0300...0x031f */ + + [0x00] = KEY_POWER, + [0x01] = KEY_SLEEP, + [0x02] = KEY_STOP, + [0x03] = KEY_OK, + [0x04] = KEY_RIGHT, + [0x05] = KEY_1, + [0x06] = KEY_2, + [0x07] = KEY_3, + [0x08] = KEY_LEFT, + [0x09] = KEY_4, + [0x0a] = KEY_5, + [0x0b] = KEY_6, + [0x0c] = KEY_UP, + [0x0d] = KEY_7, + [0x0e] = KEY_8, + [0x0f] = KEY_9, + [0x10] = KEY_DOWN, + [0x11] = KEY_TITLE, /* "OSD" - fixme */ + [0x12] = KEY_0, + [0x13] = KEY_F20, /* "16:9" - fixme */ + [0x14] = KEY_SCREEN, /* "FULL" - fixme */ + [0x15] = KEY_MUTE, + [0x16] = KEY_SUBTITLE, + [0x17] = KEY_RECORD, + [0x18] = KEY_TEXT, + [0x19] = KEY_AUDIO, + [0x1a] = KEY_RED, + [0x1b] = KEY_PREVIOUS, + [0x1c] = KEY_REWIND, + [0x1d] = KEY_PLAYPAUSE, + [0x1e] = KEY_NEXT, + [0x1f] = KEY_VOLUMEUP, + + /* code from device: 0x0340...0x0354 */ + + [0x20] = KEY_CHANNELUP, + [0x21] = KEY_F21, /* "4:3" - fixme */ + [0x22] = KEY_TV, + [0x23] = KEY_DVD, + [0x24] = KEY_VCR, + [0x25] = KEY_AUX, + [0x26] = KEY_GREEN, + [0x27] = KEY_YELLOW, + [0x28] = KEY_BLUE, + [0x29] = KEY_CHANNEL, /* "CH.LIST" */ + [0x2a] = KEY_VENDOR, /* "CI" - fixme */ + [0x2b] = KEY_VOLUMEDOWN, + [0x2c] = KEY_CHANNELDOWN, + [0x2d] = KEY_LAST, + [0x2e] = KEY_INFO, + [0x2f] = KEY_FORWARD, + [0x30] = KEY_LIST, + [0x31] = KEY_FAVORITES, + [0x32] = KEY_MENU, + [0x33] = KEY_EPG, + [0x34] = KEY_EXIT, +}; + +int fdtv_register_rc(struct firedtv *fdtv, struct device *dev) +{ + struct input_dev *idev; + int i, err; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + fdtv->remote_ctrl_dev = idev; + idev->name = "FireDTV remote control"; + idev->dev.parent = dev; + idev->evbit[0] = BIT_MASK(EV_KEY); + idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); + if (!idev->keycode) { + err = -ENOMEM; + goto fail; + } + idev->keycodesize = sizeof(keytable[0]); + idev->keycodemax = ARRAY_SIZE(keytable); + + for (i = 0; i < ARRAY_SIZE(keytable); i++) + set_bit(keytable[i], idev->keybit); + + err = input_register_device(idev); + if (err) + goto fail_free_keymap; + + return 0; + +fail_free_keymap: + kfree(idev->keycode); +fail: + input_free_device(idev); + return err; +} + +void fdtv_unregister_rc(struct firedtv *fdtv) +{ + kfree(fdtv->remote_ctrl_dev->keycode); + input_unregister_device(fdtv->remote_ctrl_dev); +} + +void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) +{ + u16 *keycode = fdtv->remote_ctrl_dev->keycode; + + if (code >= 0x0300 && code <= 0x031f) + code = keycode[code - 0x0300]; + else if (code >= 0x0340 && code <= 0x0354) + code = keycode[code - 0x0320]; + else if (code >= 0x4501 && code <= 0x451f) + code = oldtable[code - 0x4501]; + else if (code >= 0x4540 && code <= 0x4542) + code = oldtable[code - 0x4521]; + else { + printk(KERN_DEBUG "firedtv: invalid key code 0x%04x " + "from remote control\n", code); + return; + } + + input_report_key(fdtv->remote_ctrl_dev, code, 1); + input_report_key(fdtv->remote_ctrl_dev, code, 0); +} diff --git a/drivers/media/dvb/firewire/firedtv-rc.h b/drivers/media/dvb/firewire/firedtv-rc.h new file mode 100644 index 000000000000..d3e14727d3dd --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-rc.h @@ -0,0 +1,11 @@ +#ifndef _FIREDTV_RC_H +#define _FIREDTV_RC_H + +struct firedtv; +struct device; + +int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); +void fdtv_unregister_rc(struct firedtv *fdtv); +void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code); + +#endif /* _FIREDTV_RC_H */ diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h new file mode 100644 index 000000000000..2a34028ccbcd --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv.h @@ -0,0 +1,227 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _FIREDTV_H +#define _FIREDTV_H + +#include <linux/dvb/dmx.h> +#include <linux/dvb/frontend.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include <demux.h> +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> +#include <dvb_net.h> +#include <dvbdev.h> + +#include <linux/version.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) +#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w, v) +#else +#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w) +#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(x) +#endif + +/***************************************************************** + * CA message command constants from en50221_app_tags.h of libdvb + *****************************************************************/ +/* Resource Manager */ +#define TAG_PROFILE_ENQUIRY 0x9f8010 +#define TAG_PROFILE 0x9f8011 +#define TAG_PROFILE_CHANGE 0x9f8012 + +/* Application Info */ +#define TAG_APP_INFO_ENQUIRY 0x9f8020 +#define TAG_APP_INFO 0x9f8021 +#define TAG_ENTER_MENU 0x9f8022 + +/* CA Support */ +#define TAG_CA_INFO_ENQUIRY 0x9f8030 +#define TAG_CA_INFO 0x9f8031 +#define TAG_CA_PMT 0x9f8032 +#define TAG_CA_PMT_REPLY 0x9f8033 + +/* Host Control */ +#define TAG_TUNE 0x9f8400 +#define TAG_REPLACE 0x9f8401 +#define TAG_CLEAR_REPLACE 0x9f8402 +#define TAG_ASK_RELEASE 0x9f8403 + +/* Date and Time */ +#define TAG_DATE_TIME_ENQUIRY 0x9f8440 +#define TAG_DATE_TIME 0x9f8441 + +/* Man Machine Interface (MMI) */ +#define TAG_CLOSE_MMI 0x9f8800 +#define TAG_DISPLAY_CONTROL 0x9f8801 +#define TAG_DISPLAY_REPLY 0x9f8802 +#define TAG_TEXT_LAST 0x9f8803 +#define TAG_TEXT_MORE 0x9f8804 +#define TAG_KEYPAD_CONTROL 0x9f8805 +#define TAG_KEYPRESS 0x9f8806 +#define TAG_ENQUIRY 0x9f8807 +#define TAG_ANSWER 0x9f8808 +#define TAG_MENU_LAST 0x9f8809 +#define TAG_MENU_MORE 0x9f880a +#define TAG_MENU_ANSWER 0x9f880b +#define TAG_LIST_LAST 0x9f880c +#define TAG_LIST_MORE 0x9f880d +#define TAG_SUBTITLE_SEGMENT_LAST 0x9f880e +#define TAG_SUBTITLE_SEGMENT_MORE 0x9f880f +#define TAG_DISPLAY_MESSAGE 0x9f8810 +#define TAG_SCENE_END_MARK 0x9f8811 +#define TAG_SCENE_DONE 0x9f8812 +#define TAG_SCENE_CONTROL 0x9f8813 +#define TAG_SUBTITLE_DOWNLOAD_LAST 0x9f8814 +#define TAG_SUBTITLE_DOWNLOAD_MORE 0x9f8815 +#define TAG_FLUSH_DOWNLOAD 0x9f8816 +#define TAG_DOWNLOAD_REPLY 0x9f8817 + +/* Low Speed Communications */ +#define TAG_COMMS_COMMAND 0x9f8c00 +#define TAG_CONNECTION_DESCRIPTOR 0x9f8c01 +#define TAG_COMMS_REPLY 0x9f8c02 +#define TAG_COMMS_SEND_LAST 0x9f8c03 +#define TAG_COMMS_SEND_MORE 0x9f8c04 +#define TAG_COMMS_RECV_LAST 0x9f8c05 +#define TAG_COMMS_RECV_MORE 0x9f8c06 + +/* Authentication */ +#define TAG_AUTH_REQ 0x9f8200 +#define TAG_AUTH_RESP 0x9f8201 + +/* Teletext */ +#define TAG_TELETEXT_EBU 0x9f9000 + +/* Smartcard */ +#define TAG_SMARTCARD_COMMAND 0x9f8e00 +#define TAG_SMARTCARD_REPLY 0x9f8e01 +#define TAG_SMARTCARD_SEND 0x9f8e02 +#define TAG_SMARTCARD_RCV 0x9f8e03 + +/* EPG */ +#define TAG_EPG_ENQUIRY 0x9f8f00 +#define TAG_EPG_REPLY 0x9f8f01 + + +enum model_type { + FIREDTV_UNKNOWN = 0, + FIREDTV_DVB_S = 1, + FIREDTV_DVB_C = 2, + FIREDTV_DVB_T = 3, + FIREDTV_DVB_S2 = 4, +}; + +struct input_dev; +struct hpsb_iso; +struct unit_directory; + +struct firedtv { + struct dvb_adapter adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dmx_frontend frontend; + struct dvb_net dvbnet; + struct dvb_frontend fe; + + struct dvb_device *cadev; + int ca_last_command; + int ca_time_interval; + + struct mutex avc_mutex; + wait_queue_head_t avc_wait; + bool avc_reply_received; + struct work_struct remote_ctrl_work; + struct input_dev *remote_ctrl_dev; + + struct firedtv_channel { + bool active; + int pid; + } channel[16]; + struct mutex demux_mutex; + + struct unit_directory *ud; + + enum model_type type; + char subunit; + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t tone; + + int isochannel; + struct hpsb_iso *iso_handle; + + struct list_head list; + + /* needed by avc_api */ + int resp_length; + u8 respfrm[512]; +}; + +struct firewireheader { + union { + struct { + __u8 tcode:4; + __u8 sy:4; + __u8 tag:2; + __u8 channel:6; + + __u8 length_l; + __u8 length_h; + } hdr; + __u32 val; + }; +}; + +struct CIPHeader { + union { + struct { + __u8 syncbits:2; + __u8 sid:6; + __u8 dbs; + __u8 fn:2; + __u8 qpc:3; + __u8 sph:1; + __u8 rsv:2; + __u8 dbc; + __u8 syncbits2:2; + __u8 fmt:6; + __u32 fdf:24; + } cip; + __u64 val; + }; +}; + +extern const char *fdtv_model_names[]; +extern struct list_head fdtv_list; +extern spinlock_t fdtv_list_lock; + +struct device; + +/* firedtv-dvb.c */ +int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed); +int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +int fdtv_dvbdev_init(struct firedtv *fdtv, struct device *dev); + +/* firedtv-fe.c */ +void fdtv_frontend_init(struct firedtv *fdtv); + +/* firedtv-iso.c */ +int setup_iso_channel(struct firedtv *fdtv); +void tear_down_iso_channel(struct firedtv *fdtv); + +#endif /* _FIREDTV_H */ -- cgit v1.2.3 From 154907957f9391b1af997b57507b16c018cc4995 Mon Sep 17 00:00:00 2001 From: Stefan Richter <stefanr@s5r6.in-berlin.de> Date: Mon, 23 Feb 2009 14:21:10 +0100 Subject: firedtv: massive refactoring Combination of the following changes: Mon, 23 Feb 2009 14:21:10 +0100 (CET) firedtv: reinstate debug logging option Henrik Kurelid tells me that FCP debug logging (which I removed during cleanups) is still useful when working on driver issues together with end users. So bring it back in an updated form with only 60% of the original code footprint. Logging can be enabled with # echo -1 > /sys/module/firedtv/parameters/debug 1 instead of -1 enables only FCP header logging, 2 instead of -1 enables only hexdumps of the entire FCP frames. 0 switches logging off again. Fri, 20 Feb 2009 20:54:27 +0100 (CET) firedtv: build fix for INPUT=m and DVB_FIREDTV=y Thu, 19 Feb 2009 20:40:39 +0100 firedtv: use msecs_to_jiffies Pointed out by Mauro Carvalho Chehab. Sun Feb 15 20:50:46 CET 2009 firedtv: some more housekeeping Fix an old checkpatch warning and a new compiler warning. Sun Feb 15 15:33:17 CET 2009 firedtv: rename a file once more At the moment, about a third of avc.c is specific to FireDTVs rather than generic AV/C code. Rename it to firedtv-avc.c. Sun Feb 15 15:33:17 CET 2009 firedtv: dvb demux: more compact channels backing store Replace struct firedtv_channel { bool active; int pid; } channel[16]; by unsigned long channel_active; u16 channel_pid[16];. Sun Feb 15 15:33:17 CET 2009 firedtv: dvb demux: some simplifications c->active was unnecessarily cleared twice. Also, by marking the channel inactive before the for loop, the loop becomes identical with fdtv_channel_collect(). Sun Feb 15 15:33:17 CET 2009 firedtv: dvb demux: remove a bogus loop This loop is unnecessary because - only active channel[].pid's will be sent to the device, - when a channel is activated, its pid is set to dvbdmxfeed->pid. Perhaps the original code was there because it was initially not fully covered by the fdtv->demux_mutex. Sun Feb 15 15:33:17 CET 2009 firedtv: dvb demux: fix mutex protection fdtv_start_feed() accessed the channel list unsafely. Fully serialize it with itself and fdtv_stop_feed(). Sun Feb 15 15:33:17 CET 2009 firedtv: dvb demux: fix missing braces Original code was: ... case DMX_TS_PES_OTHER: //Dirty fix to keep firesat->channel pid-list up to date for(k=0;k<16;k++){ if(firesat->channel[k].active == 0) firesat->channel[k].pid = dvbdmxfeed->pid; break; } channel = firesat_channel_allocate(firesat); break; default: ... Looks bogus in several respects. For now let's just add braces to the if because that seems to be what the author meant. Sun Feb 15 15:33:17 CET 2009 firedtv: allow build without input subsystem !CONFIG_INPUT is very unlikely on systems on which firedtv is of interest. But we can easily support it. Sun Feb 15 15:33:17 CET 2009 firedtv: replace EXTRA_CFLAGS by ccflags The former are deprecated. The latter can depend on Kconfig variables. Sun Feb 15 15:33:17 CET 2009 firedtv: concentrate ieee1394 dependencies Move the entire interface with drivers/ieee1394 to firedtv-1394.c. Move 1394-independent module initialization code to firedtv-dvb.c. This prepares interfacing with drivers/firewire. Sun Feb 15 15:33:17 CET 2009 firedtv: amend Kconfig menu prompt Sun Feb 15 15:33:17 CET 2009 firedtv: remove kernel version compatibility macro Sun Feb 15 15:33:17 CET 2009 firedtv: combine header files avc.h and firedtv-*.h are small and currently not shared with other drivers, hence concatenate them all into firedtv.h. Sun Feb 15 15:33:17 CET 2009 firedtv: misc style touch-ups Standardize on lower-case hexadecimal constants. Adjust whitespace. Omit unnecessary pointer type casts and an unnecessary list head initialization. Use dev_printk. Wed Feb 11 21:21:04 CET 2009 firedtv: avc, ci: remove unused constants Wed Feb 11 21:21:04 CET 2009 firedtv: avc: remove bitfields from read descriptor response operands Don't use bitfields in struct types of on-the-wire data. Wed Feb 11 21:21:04 CET 2009 firedtv: avc: remove bitfields from DSD command operands Don't use bitfields in struct types of on-the-wire data. Wed Feb 11 21:21:04 CET 2009 firedtv: avc: header file cleanup Remove unused constants and declarations. Move privately used constants into .c files. Wed Feb 11 21:21:04 CET 2009 firedtv: avc: remove bitfields from FCP frame types Don't use bitfields in struct types of on-the-wire data. Also move many privately used constants from avc.h to avc.c and remove some unused constants. Sun, 18 Jan 2009 16:30:00 +0100 (CET) firedtv: avc: fix offset in avc_tuner_get_ts The parentheses were wrong. It didn't matter though because this code only writes a 0 into an area which is already initialized to 0. Sun, 18 Jan 2009 16:30:00 +0100 (CET) firedtv: avc: reduce stack usage, remove two typedefs It is safe to share a memory buffer for command frame and response frame because the response data come in after the command frame was last used. Even less stack would be required if only the actual required frame size instead of the entire FCP register size was allocated. Also, rename the defined types AVCCmdFrm and AVCRspFrm to struct avc_command_frame and struct avc_response_frame. TODO: Remove the bitfields in these types. Sun, 18 Jan 2009 16:30:00 +0100 (CET) firedtv: cmp: move code to avc Sun, 18 Jan 2009 16:30:00 +0100 (CET) firedtv: iso: move code to firedtv-1394 Sun, 18 Jan 2009 16:30:00 +0100 (CET) firedtv: iso: remove unnecessary struct type definitions Sun, 18 Jan 2009 16:30:00 +0100 (CET) firedtv: iso: style changes and fixlets Add cleanup after failure in setup_iso_channel. Replace printk() by dv_err(). Decrease indentation level in rawiso_activity_cb(). Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/Kconfig | 2 + drivers/media/dvb/firewire/Kconfig | 26 +- drivers/media/dvb/firewire/Makefile | 17 +- drivers/media/dvb/firewire/avc.c | 1051 ----------------------- drivers/media/dvb/firewire/avc.h | 432 ---------- drivers/media/dvb/firewire/cmp.c | 171 ---- drivers/media/dvb/firewire/cmp.h | 9 - drivers/media/dvb/firewire/firedtv-1394.c | 332 ++++---- drivers/media/dvb/firewire/firedtv-avc.c | 1315 +++++++++++++++++++++++++++++ drivers/media/dvb/firewire/firedtv-ci.c | 93 +- drivers/media/dvb/firewire/firedtv-ci.h | 9 - drivers/media/dvb/firewire/firedtv-dvb.c | 328 ++++--- drivers/media/dvb/firewire/firedtv-fe.c | 61 +- drivers/media/dvb/firewire/firedtv-iso.c | 111 --- drivers/media/dvb/firewire/firedtv-rc.c | 1 - drivers/media/dvb/firewire/firedtv-rc.h | 11 - drivers/media/dvb/firewire/firedtv.h | 257 +++--- 17 files changed, 1895 insertions(+), 2331 deletions(-) delete mode 100644 drivers/media/dvb/firewire/avc.c delete mode 100644 drivers/media/dvb/firewire/avc.h delete mode 100644 drivers/media/dvb/firewire/cmp.c delete mode 100644 drivers/media/dvb/firewire/cmp.h create mode 100644 drivers/media/dvb/firewire/firedtv-avc.c delete mode 100644 drivers/media/dvb/firewire/firedtv-ci.h delete mode 100644 drivers/media/dvb/firewire/firedtv-iso.c delete mode 100644 drivers/media/dvb/firewire/firedtv-rc.h (limited to 'drivers') diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 5a74c5c62f15..b0198691892a 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -51,6 +51,8 @@ comment "Supported SDMC DM1105 Adapters" depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/dm1105/Kconfig" +comment "Supported FireWire (IEEE 1394) Adapters" + depends on DVB_CORE && IEEE1394 source "drivers/media/dvb/firewire/Kconfig" comment "Supported DVB Frontends" diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig index 03d25ad10350..69028253e984 100644 --- a/drivers/media/dvb/firewire/Kconfig +++ b/drivers/media/dvb/firewire/Kconfig @@ -1,12 +1,22 @@ config DVB_FIREDTV - tristate "FireDTV (FireWire attached DVB receivers)" - depends on DVB_CORE && IEEE1394 && INPUT + tristate "FireDTV and FloppyDTV" + depends on DVB_CORE && IEEE1394 help - Support for DVB receivers from Digital Everywhere, known as FireDTV - and FloppyDTV, which are connected via IEEE 1394 (FireWire). + Support for DVB receivers from Digital Everywhere + which are connected via IEEE 1394 (FireWire). - These devices don't have an MPEG decoder built in, so you need - an external software decoder to watch TV. + These devices don't have an MPEG decoder built in, + so you need an external software decoder to watch TV. - To compile this driver as a module, say M here: the module will be - called firedtv. + To compile this driver as a module, say M here: + the module will be called firedtv. + +if DVB_FIREDTV + +config DVB_FIREDTV_IEEE1394 + def_bool IEEE1394 + +config DVB_FIREDTV_INPUT + def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) + +endif # DVB_FIREDTV diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile index 628dacd10daf..2034695ba194 100644 --- a/drivers/media/dvb/firewire/Makefile +++ b/drivers/media/dvb/firewire/Makefile @@ -1,13 +1,8 @@ -firedtv-objs := firedtv-1394.o \ - firedtv-dvb.o \ - firedtv-fe.o \ - firedtv-iso.o \ - avc.o \ - cmp.o \ - firedtv-rc.o \ - firedtv-ci.o - obj-$(CONFIG_DVB_FIREDTV) += firedtv.o -EXTRA_CFLAGS := -Idrivers/ieee1394 -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o +firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o +firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o + +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-$(CONFIG_DVB_FIREDTV_IEEE1394) += -Idrivers/ieee1394 diff --git a/drivers/media/dvb/firewire/avc.c b/drivers/media/dvb/firewire/avc.c deleted file mode 100644 index 847a537b1f58..000000000000 --- a/drivers/media/dvb/firewire/avc.c +++ /dev/null @@ -1,1051 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Ben Backx <ben@bbackx.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/bug.h> -#include <linux/crc32.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/moduleparam.h> -#include <linux/mutex.h> -#include <linux/string.h> -#include <linux/wait.h> -#include <linux/workqueue.h> - -#include <ieee1394_transactions.h> -#include <nodemgr.h> - -#include "avc.h" -#include "firedtv.h" -#include "firedtv-rc.h" - -#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL - -static int __avc_write(struct firedtv *fdtv, - const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) -{ - int err, retry; - - if (RspFrm) - fdtv->avc_reply_received = false; - - for (retry = 0; retry < 6; retry++) { - err = hpsb_node_write(fdtv->ud->ne, FCP_COMMAND_REGISTER, - (quadlet_t *)CmdFrm, CmdFrm->length); - if (err) { - fdtv->avc_reply_received = true; - dev_err(&fdtv->ud->device, - "FCP command write failed\n"); - return err; - } - - if (!RspFrm) - return 0; - - /* - * AV/C specs say that answers should be sent within 150 ms. - * Time out after 200 ms. - */ - if (wait_event_timeout(fdtv->avc_wait, - fdtv->avc_reply_received, - HZ / 5) != 0) { - memcpy(RspFrm, fdtv->respfrm, fdtv->resp_length); - RspFrm->length = fdtv->resp_length; - - return 0; - } - } - dev_err(&fdtv->ud->device, "FCP response timed out\n"); - return -ETIMEDOUT; -} - -static int avc_write(struct firedtv *fdtv, - const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) -{ - int ret; - - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; - - ret = __avc_write(fdtv, CmdFrm, RspFrm); - - mutex_unlock(&fdtv->avc_mutex); - return ret; -} - -int avc_recv(struct firedtv *fdtv, u8 *data, size_t length) -{ - AVCRspFrm *RspFrm = (AVCRspFrm *)data; - - if (length >= 8 && - RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && - RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && - RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && - RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { - if (RspFrm->resp == CHANGED) { - fdtv_handle_rc(fdtv, - RspFrm->operand[4] << 8 | RspFrm->operand[5]); - schedule_work(&fdtv->remote_ctrl_work); - } else if (RspFrm->resp != INTERIM) { - dev_info(&fdtv->ud->device, - "remote control result = %d\n", RspFrm->resp); - } - return 0; - } - - if (fdtv->avc_reply_received) { - dev_err(&fdtv->ud->device, - "received out-of-order AVC response, ignored\n"); - return -EIO; - } - - memcpy(fdtv->respfrm, data, length); - fdtv->resp_length = length; - - fdtv->avc_reply_received = true; - wake_up(&fdtv->avc_wait); - - return 0; -} - -/* - * tuning command for setting the relative LNB frequency - * (not supported by the AVC standard) - */ -static void avc_tuner_tuneqpsk(struct firedtv *fdtv, - struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) -{ - CmdFrm->opcode = VENDOR; - - CmdFrm->operand[0] = SFE_VENDOR_DE_COMPANYID_0; - CmdFrm->operand[1] = SFE_VENDOR_DE_COMPANYID_1; - CmdFrm->operand[2] = SFE_VENDOR_DE_COMPANYID_2; - CmdFrm->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; - - CmdFrm->operand[4] = (params->frequency >> 24) & 0xff; - CmdFrm->operand[5] = (params->frequency >> 16) & 0xff; - CmdFrm->operand[6] = (params->frequency >> 8) & 0xff; - CmdFrm->operand[7] = params->frequency & 0xff; - - CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; - CmdFrm->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; - - switch(params->u.qpsk.fec_inner) { - case FEC_1_2: - CmdFrm->operand[10] = 0x1; break; - case FEC_2_3: - CmdFrm->operand[10] = 0x2; break; - case FEC_3_4: - CmdFrm->operand[10] = 0x3; break; - case FEC_5_6: - CmdFrm->operand[10] = 0x4; break; - case FEC_7_8: - CmdFrm->operand[10] = 0x5; break; - case FEC_4_5: - case FEC_8_9: - case FEC_AUTO: - default: - CmdFrm->operand[10] = 0x0; - } - - if (fdtv->voltage == 0xff) - CmdFrm->operand[11] = 0xff; - else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ - CmdFrm->operand[11] = 0; - else - CmdFrm->operand[11] = 1; - - if (fdtv->tone == 0xff) - CmdFrm->operand[12] = 0xff; - else if (fdtv->tone == SEC_TONE_ON) /* band */ - CmdFrm->operand[12] = 1; - else - CmdFrm->operand[12] = 0; - - if (fdtv->type == FIREDTV_DVB_S2) { - CmdFrm->operand[13] = 0x1; - CmdFrm->operand[14] = 0xff; - CmdFrm->operand[15] = 0xff; - CmdFrm->length = 20; - } else { - CmdFrm->length = 16; - } -} - -static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, - AVCCmdFrm *CmdFrm) -{ - M_VALID_FLAGS flags; - - flags.Bits.Modulation = params->u.qam.modulation != QAM_AUTO; - flags.Bits.FEC_inner = params->u.qam.fec_inner != FEC_AUTO; - flags.Bits.FEC_outer = 0; - flags.Bits.Symbol_Rate = 1; - flags.Bits.Frequency = 1; - flags.Bits.Orbital_Pos = 0; - flags.Bits.Polarisation = 0; - flags.Bits.reserved_fields = 0; - flags.Bits.reserved1 = 0; - flags.Bits.Network_ID = 0; - - CmdFrm->opcode = DSD; - - CmdFrm->operand[0] = 0; /* source plug */ - CmdFrm->operand[1] = 0xd2; /* subfunction replace */ - CmdFrm->operand[2] = 0x20; /* system id = DVB */ - CmdFrm->operand[3] = 0x00; /* antenna number */ - /* system_specific_multiplex selection_length */ - CmdFrm->operand[4] = 0x11; - CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ - CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ - CmdFrm->operand[7] = 0x00; - CmdFrm->operand[8] = 0x00; - CmdFrm->operand[9] = 0x00; - CmdFrm->operand[10] = 0x00; - - CmdFrm->operand[11] = - (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); - CmdFrm->operand[12] = - ((params->frequency / 4000) >> 8) & 0xff; - CmdFrm->operand[13] = (params->frequency / 4000) & 0xff; - CmdFrm->operand[14] = - ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; - CmdFrm->operand[15] = - ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; - CmdFrm->operand[16] = - ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; - CmdFrm->operand[17] = 0x00; - - switch (params->u.qpsk.fec_inner) { - case FEC_1_2: - CmdFrm->operand[18] = 0x1; break; - case FEC_2_3: - CmdFrm->operand[18] = 0x2; break; - case FEC_3_4: - CmdFrm->operand[18] = 0x3; break; - case FEC_5_6: - CmdFrm->operand[18] = 0x4; break; - case FEC_7_8: - CmdFrm->operand[18] = 0x5; break; - case FEC_8_9: - CmdFrm->operand[18] = 0x6; break; - case FEC_4_5: - CmdFrm->operand[18] = 0x8; break; - case FEC_AUTO: - default: - CmdFrm->operand[18] = 0x0; - } - switch (params->u.qam.modulation) { - case QAM_16: - CmdFrm->operand[19] = 0x08; break; - case QAM_32: - CmdFrm->operand[19] = 0x10; break; - case QAM_64: - CmdFrm->operand[19] = 0x18; break; - case QAM_128: - CmdFrm->operand[19] = 0x20; break; - case QAM_256: - CmdFrm->operand[19] = 0x28; break; - case QAM_AUTO: - default: - CmdFrm->operand[19] = 0x00; - } - CmdFrm->operand[20] = 0x00; - CmdFrm->operand[21] = 0x00; - /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ - CmdFrm->operand[22] = 0x00; - - CmdFrm->length = 28; -} - -static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, - AVCCmdFrm *CmdFrm) -{ - M_VALID_FLAGS flags; - - flags.Bits_T.GuardInterval = - params->u.ofdm.guard_interval != GUARD_INTERVAL_AUTO; - flags.Bits_T.CodeRateLPStream = - params->u.ofdm.code_rate_LP != FEC_AUTO; - flags.Bits_T.CodeRateHPStream = - params->u.ofdm.code_rate_HP != FEC_AUTO; - flags.Bits_T.HierarchyInfo = - params->u.ofdm.hierarchy_information != HIERARCHY_AUTO; - flags.Bits_T.Constellation = - params->u.ofdm.constellation != QAM_AUTO; - flags.Bits_T.Bandwidth = - params->u.ofdm.bandwidth != BANDWIDTH_AUTO; - flags.Bits_T.CenterFrequency = 1; - flags.Bits_T.reserved1 = 0; - flags.Bits_T.reserved2 = 0; - flags.Bits_T.OtherFrequencyFlag = 0; - flags.Bits_T.TransmissionMode = - params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO; - flags.Bits_T.NetworkId = 0; - - CmdFrm->opcode = DSD; - - CmdFrm->operand[0] = 0; /* source plug */ - CmdFrm->operand[1] = 0xd2; /* subfunction replace */ - CmdFrm->operand[2] = 0x20; /* system id = DVB */ - CmdFrm->operand[3] = 0x00; /* antenna number */ - /* system_specific_multiplex selection_length */ - CmdFrm->operand[4] = 0x0c; - CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ - CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ - CmdFrm->operand[7] = 0x0; - CmdFrm->operand[8] = (params->frequency / 10) >> 24; - CmdFrm->operand[9] = ((params->frequency / 10) >> 16) & 0xff; - CmdFrm->operand[10] = ((params->frequency / 10) >> 8) & 0xff; - CmdFrm->operand[11] = (params->frequency / 10) & 0xff; - - switch (params->u.ofdm.bandwidth) { - case BANDWIDTH_7_MHZ: - CmdFrm->operand[12] = 0x20; break; - case BANDWIDTH_8_MHZ: - case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ - case BANDWIDTH_AUTO: - default: - CmdFrm->operand[12] = 0x00; - } - switch (params->u.ofdm.constellation) { - case QAM_16: - CmdFrm->operand[13] = 1 << 6; break; - case QAM_64: - CmdFrm->operand[13] = 2 << 6; break; - case QPSK: - default: - CmdFrm->operand[13] = 0x00; - } - switch (params->u.ofdm.hierarchy_information) { - case HIERARCHY_1: - CmdFrm->operand[13] |= 1 << 3; break; - case HIERARCHY_2: - CmdFrm->operand[13] |= 2 << 3; break; - case HIERARCHY_4: - CmdFrm->operand[13] |= 3 << 3; break; - case HIERARCHY_AUTO: - case HIERARCHY_NONE: - default: - break; - } - switch (params->u.ofdm.code_rate_HP) { - case FEC_2_3: - CmdFrm->operand[13] |= 1; break; - case FEC_3_4: - CmdFrm->operand[13] |= 2; break; - case FEC_5_6: - CmdFrm->operand[13] |= 3; break; - case FEC_7_8: - CmdFrm->operand[13] |= 4; break; - case FEC_1_2: - default: - break; - } - switch (params->u.ofdm.code_rate_LP) { - case FEC_2_3: - CmdFrm->operand[14] = 1 << 5; break; - case FEC_3_4: - CmdFrm->operand[14] = 2 << 5; break; - case FEC_5_6: - CmdFrm->operand[14] = 3 << 5; break; - case FEC_7_8: - CmdFrm->operand[14] = 4 << 5; break; - case FEC_1_2: - default: - CmdFrm->operand[14] = 0x00; break; - } - switch (params->u.ofdm.guard_interval) { - case GUARD_INTERVAL_1_16: - CmdFrm->operand[14] |= 1 << 3; break; - case GUARD_INTERVAL_1_8: - CmdFrm->operand[14] |= 2 << 3; break; - case GUARD_INTERVAL_1_4: - CmdFrm->operand[14] |= 3 << 3; break; - case GUARD_INTERVAL_1_32: - case GUARD_INTERVAL_AUTO: - default: - break; - } - switch (params->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_8K: - CmdFrm->operand[14] |= 1 << 1; break; - case TRANSMISSION_MODE_2K: - case TRANSMISSION_MODE_AUTO: - default: - break; - } - - CmdFrm->operand[15] = 0x00; /* network_ID[0] */ - CmdFrm->operand[16] = 0x00; /* network_ID[1] */ - /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ - CmdFrm->operand[17] = 0x00; - - CmdFrm->length = 24; -} - -int avc_tuner_dsd(struct firedtv *fdtv, - struct dvb_frontend_parameters *params) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - - switch (fdtv->type) { - case FIREDTV_DVB_S: - case FIREDTV_DVB_S2: - avc_tuner_tuneqpsk(fdtv, params, &CmdFrm); break; - case FIREDTV_DVB_C: - avc_tuner_dsd_dvb_c(params, &CmdFrm); break; - case FIREDTV_DVB_T: - avc_tuner_dsd_dvb_t(params, &CmdFrm); break; - default: - BUG(); - } - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - msleep(500); -#if 0 - /* FIXME: */ - /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ - if(status) - *status=RspFrm.operand[2]; -#endif - return 0; -} - -int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int pos, k; - - if (pidc > 16 && pidc != 0xff) - return -EINVAL; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = DSD; - - CmdFrm.operand[0] = 0; // source plug - CmdFrm.operand[1] = 0xD2; // subfunction replace - CmdFrm.operand[2] = 0x20; // system id = DVB - CmdFrm.operand[3] = 0x00; // antenna number - CmdFrm.operand[4] = 0x00; // system_specific_multiplex selection_length - CmdFrm.operand[5] = pidc; // Nr_of_dsd_sel_specs - - pos = 6; - if (pidc != 0xff) - for (k = 0; k < pidc; k++) { - CmdFrm.operand[pos++] = 0x13; // flowfunction relay - CmdFrm.operand[pos++] = 0x80; // dsd_sel_spec_valid_flags -> PID - CmdFrm.operand[pos++] = (pid[k] >> 8) & 0x1F; - CmdFrm.operand[pos++] = pid[k] & 0xFF; - CmdFrm.operand[pos++] = 0x00; // tableID - CmdFrm.operand[pos++] = 0x00; // filter_length - } - - CmdFrm.length = ALIGN(3 + pos, 4); - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - msleep(50); - return 0; -} - -int avc_tuner_get_ts(struct firedtv *fdtv) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = DSIT; - - CmdFrm.operand[0] = 0; // source plug - CmdFrm.operand[1] = 0xD2; // subfunction replace - CmdFrm.operand[2] = 0xFF; //status - CmdFrm.operand[3] = 0x20; // system id = DVB - CmdFrm.operand[4] = 0x00; // antenna number - CmdFrm.operand[5] = 0x0; // system_specific_search_flags - CmdFrm.operand[6] = (fdtv->type == FIREDTV_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length - CmdFrm.operand[7] = 0x00; // valid_flags [0] - CmdFrm.operand[8] = 0x00; // valid_flags [1] - CmdFrm.operand[7 + (fdtv->type == FIREDTV_DVB_T)?0x0c:0x11] = 0x00; // nr_of_dsit_sel_specs (always 0) - - CmdFrm.length = (fdtv->type == FIREDTV_DVB_T)?24:28; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - msleep(250); - return 0; -} - -int avc_identify_subunit(struct firedtv *fdtv) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm,0,sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; // tuner - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = READ_DESCRIPTOR; - - CmdFrm.operand[0]=DESCRIPTOR_SUBUNIT_IDENTIFIER; - CmdFrm.operand[1]=0xff; - CmdFrm.operand[2]=0x00; - CmdFrm.operand[3]=0x00; // length highbyte - CmdFrm.operand[4]=0x08; // length lowbyte - CmdFrm.operand[5]=0x00; // offset highbyte - CmdFrm.operand[6]=0x0d; // offset lowbyte - - CmdFrm.length=12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if ((RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) || - (RspFrm.operand[3] << 8) + RspFrm.operand[4] != 8) { - dev_err(&fdtv->ud->device, - "cannot read subunit identifier\n"); - return -EINVAL; - } - return 0; -} - -int avc_tuner_status(struct firedtv *fdtv, - ANTENNA_INPUT_INFO *antenna_input_info) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int length; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts=AVC; - CmdFrm.ctype=CONTROL; - CmdFrm.sutyp=0x05; // tuner - CmdFrm.suid=fdtv->subunit; - CmdFrm.opcode=READ_DESCRIPTOR; - - CmdFrm.operand[0]=DESCRIPTOR_TUNER_STATUS; - CmdFrm.operand[1]=0xff; //read_result_status - CmdFrm.operand[2]=0x00; // reserver - CmdFrm.operand[3]=0;//sizeof(ANTENNA_INPUT_INFO) >> 8; - CmdFrm.operand[4]=0;//sizeof(ANTENNA_INPUT_INFO) & 0xFF; - CmdFrm.operand[5]=0x00; - CmdFrm.operand[6]=0x00; - CmdFrm.length=12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if (RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { - dev_err(&fdtv->ud->device, "cannot read tuner status\n"); - return -EINVAL; - } - - length = RspFrm.operand[9]; - if (RspFrm.operand[1] != 0x10 || length != sizeof(ANTENNA_INPUT_INFO)) { - dev_err(&fdtv->ud->device, "got invalid tuner status\n"); - return -EINVAL; - } - - memcpy(antenna_input_info, &RspFrm.operand[10], length); - return 0; -} - -int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, - char conttone, char nrdiseq, - struct dvb_diseqc_master_cmd *diseqcmd) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int i, j, k; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts=AVC; - CmdFrm.ctype=CONTROL; - CmdFrm.sutyp=0x05; - CmdFrm.suid=fdtv->subunit; - CmdFrm.opcode=VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_LNB_CONTROL; - - CmdFrm.operand[4]=voltage; - CmdFrm.operand[5]=nrdiseq; - - i=6; - - for (j = 0; j < nrdiseq; j++) { - CmdFrm.operand[i++] = diseqcmd[j].msg_len; - - for (k = 0; k < diseqcmd[j].msg_len; k++) - CmdFrm.operand[i++] = diseqcmd[j].msg[k]; - } - - CmdFrm.operand[i++]=burst; - CmdFrm.operand[i++]=conttone; - - CmdFrm.length = ALIGN(3 + i, 4); - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if (RspFrm.resp != ACCEPTED) { - dev_err(&fdtv->ud->device, "LNB control failed\n"); - return -EINVAL; - } - - return 0; -} - -int avc_register_remote_control(struct firedtv *fdtv) -{ - AVCCmdFrm CmdFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - - CmdFrm.cts = AVC; - CmdFrm.ctype = NOTIFY; - CmdFrm.sutyp = 0x1f; - CmdFrm.suid = 0x7; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0] = SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1] = SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2] = SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; - - CmdFrm.length = 8; - - return avc_write(fdtv, &CmdFrm, NULL); -} - -void avc_remote_ctrl_work(struct work_struct *work) -{ - struct firedtv *fdtv = - container_of(work, struct firedtv, remote_ctrl_work); - - /* Should it be rescheduled in failure cases? */ - avc_register_remote_control(fdtv); -} - -#if 0 /* FIXME: unused */ -int avc_tuner_host2ca(struct firedtv *fdtv) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - return 0; -} -#endif - -static int get_ca_object_pos(AVCRspFrm *RspFrm) -{ - int length = 1; - - /* Check length of length field */ - if (RspFrm->operand[7] & 0x80) - length = (RspFrm->operand[7] & 0x7f) + 1; - return length + 7; -} - -static int get_ca_object_length(AVCRspFrm *RspFrm) -{ -#if 0 /* FIXME: unused */ - int size = 0; - int i; - - if (RspFrm->operand[7] & 0x80) - for (i = 0; i < (RspFrm->operand[7] & 0x7f); i++) { - size <<= 8; - size += RspFrm->operand[8 + i]; - } -#endif - return RspFrm->operand[7]; -} - -int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int pos; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag - CmdFrm.length = 12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - /* FIXME: check response code and validate response data */ - - pos = get_ca_object_pos(&RspFrm); - app_info[0] = (TAG_APP_INFO >> 16) & 0xFF; - app_info[1] = (TAG_APP_INFO >> 8) & 0xFF; - app_info[2] = (TAG_APP_INFO >> 0) & 0xFF; - app_info[3] = 6 + RspFrm.operand[pos + 4]; - app_info[4] = 0x01; - memcpy(&app_info[5], &RspFrm.operand[pos], 5 + RspFrm.operand[pos + 4]); - *len = app_info[3] + 4; - - return 0; -} - -int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int pos; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag - CmdFrm.length = 12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - pos = get_ca_object_pos(&RspFrm); - app_info[0] = (TAG_CA_INFO >> 16) & 0xFF; - app_info[1] = (TAG_CA_INFO >> 8) & 0xFF; - app_info[2] = (TAG_CA_INFO >> 0) & 0xFF; - app_info[3] = 2; - app_info[4] = RspFrm.operand[pos + 0]; - app_info[5] = RspFrm.operand[pos + 1]; - *len = app_info[3] + 4; - - return 0; -} - -int avc_ca_reset(struct firedtv *fdtv) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_RESET; // ca tag - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 1; // length - CmdFrm.operand[8] = 0; // force hardware reset - CmdFrm.length = 12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - return 0; -} - -int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - int list_management; - int program_info_length; - int pmt_cmd_id; - int read_pos; - int write_pos; - int es_info_length; - int crc32_csum; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = CONTROL; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - if (msg[0] != LIST_MANAGEMENT_ONLY) { - dev_info(&fdtv->ud->device, - "forcing list_management to ONLY\n"); - msg[0] = LIST_MANAGEMENT_ONLY; - } - // We take the cmd_id from the programme level only! - list_management = msg[0]; - program_info_length = ((msg[4] & 0x0F) << 8) + msg[5]; - if (program_info_length > 0) - program_info_length--; // Remove pmt_cmd_id - pmt_cmd_id = msg[6]; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_PMT; // ca tag - CmdFrm.operand[6] = 0; // more/last - //CmdFrm.operand[7] = XXXprogram_info_length + 17; // length - CmdFrm.operand[8] = list_management; - CmdFrm.operand[9] = 0x01; // pmt_cmd=OK_descramble - - // TS program map table - - // Table id=2 - CmdFrm.operand[10] = 0x02; - // Section syntax + length - CmdFrm.operand[11] = 0x80; - //CmdFrm.operand[12] = XXXprogram_info_length + 12; - // Program number - CmdFrm.operand[13] = msg[1]; - CmdFrm.operand[14] = msg[2]; - // Version number=0 + current/next=1 - CmdFrm.operand[15] = 0x01; - // Section number=0 - CmdFrm.operand[16] = 0x00; - // Last section number=0 - CmdFrm.operand[17] = 0x00; - // PCR_PID=1FFF - CmdFrm.operand[18] = 0x1F; - CmdFrm.operand[19] = 0xFF; - // Program info length - CmdFrm.operand[20] = (program_info_length >> 8); - CmdFrm.operand[21] = (program_info_length & 0xFF); - // CA descriptors at programme level - read_pos = 6; - write_pos = 22; - if (program_info_length > 0) { - pmt_cmd_id = msg[read_pos++]; - if (pmt_cmd_id != 1 && pmt_cmd_id != 4) - dev_err(&fdtv->ud->device, - "invalid pmt_cmd_id %d\n", pmt_cmd_id); - - memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], - program_info_length); - read_pos += program_info_length; - write_pos += program_info_length; - } - while (read_pos < length) { - CmdFrm.operand[write_pos++] = msg[read_pos++]; - CmdFrm.operand[write_pos++] = msg[read_pos++]; - CmdFrm.operand[write_pos++] = msg[read_pos++]; - es_info_length = - ((msg[read_pos] & 0x0F) << 8) + msg[read_pos + 1]; - read_pos += 2; - if (es_info_length > 0) - es_info_length--; // Remove pmt_cmd_id - CmdFrm.operand[write_pos++] = es_info_length >> 8; - CmdFrm.operand[write_pos++] = es_info_length & 0xFF; - if (es_info_length > 0) { - pmt_cmd_id = msg[read_pos++]; - if (pmt_cmd_id != 1 && pmt_cmd_id != 4) - dev_err(&fdtv->ud->device, - "invalid pmt_cmd_id %d " - "at stream level\n", pmt_cmd_id); - - memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], - es_info_length); - read_pos += es_info_length; - write_pos += es_info_length; - } - } - - // CRC - CmdFrm.operand[write_pos++] = 0x00; - CmdFrm.operand[write_pos++] = 0x00; - CmdFrm.operand[write_pos++] = 0x00; - CmdFrm.operand[write_pos++] = 0x00; - - CmdFrm.operand[7] = write_pos - 8; - CmdFrm.operand[12] = write_pos - 13; - - crc32_csum = crc32_be(0, &CmdFrm.operand[10], - CmdFrm.operand[12] - 1); - CmdFrm.operand[write_pos - 4] = (crc32_csum >> 24) & 0xFF; - CmdFrm.operand[write_pos - 3] = (crc32_csum >> 16) & 0xFF; - CmdFrm.operand[write_pos - 2] = (crc32_csum >> 8) & 0xFF; - CmdFrm.operand[write_pos - 1] = (crc32_csum >> 0) & 0xFF; - - CmdFrm.length = ALIGN(3 + write_pos, 4); - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - if (RspFrm.resp != ACCEPTED) { - dev_err(&fdtv->ud->device, - "CA PMT failed with response 0x%x\n", RspFrm.resp); - return -EFAULT; - } - - return 0; -} - -int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; // ca tag - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - /* FIXME: check response code and validate response data */ - - *interval = RspFrm.operand[get_ca_object_pos(&RspFrm)]; - - return 0; -} - -int avc_ca_enter_menu(struct firedtv *fdtv) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - return 0; -} - -int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) -{ - AVCCmdFrm CmdFrm; - AVCRspFrm RspFrm; - - memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); - CmdFrm.cts = AVC; - CmdFrm.ctype = STATUS; - CmdFrm.sutyp = 0x5; - CmdFrm.suid = fdtv->subunit; - CmdFrm.opcode = VENDOR; - - CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; - CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; - CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; - CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; - CmdFrm.operand[4] = 0; // slot - CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_MMI; - CmdFrm.operand[6] = 0; // more/last - CmdFrm.operand[7] = 0; // length - CmdFrm.length = 12; - - if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) - return -EIO; - - /* FIXME: check response code and validate response data */ - - *len = get_ca_object_length(&RspFrm); - memcpy(mmi_object, &RspFrm.operand[get_ca_object_pos(&RspFrm)], *len); - - return 0; -} diff --git a/drivers/media/dvb/firewire/avc.h b/drivers/media/dvb/firewire/avc.h deleted file mode 100644 index 168f371dbde0..000000000000 --- a/drivers/media/dvb/firewire/avc.h +++ /dev/null @@ -1,432 +0,0 @@ -/* - * AV/C API - * - * Copyright (C) 2000 Manfred Weihs - * Copyright (C) 2003 Philipp Gutgsell <0014guph@edu.fh-kaernten.ac.at> - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Ben Backx <ben@bbackx.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This is based on code written by Peter Halwachs, Thomas Groiss and - * Andreas Monitzer. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#ifndef _AVC_API_H -#define _AVC_API_H - -#include <linux/types.h> - -/************************************************************* - Constants from EN510221 -**************************************************************/ -#define LIST_MANAGEMENT_ONLY 0x03 - -/************************************************************ - definition of structures -*************************************************************/ -typedef struct { - int Nr_SourcePlugs; - int Nr_DestinationPlugs; -} TunerInfo; - - -/*********************************************** - - supported cts - -************************************************/ - -#define AVC 0x0 - -// FCP command frame with ctype = 0x0 is AVC command frame - -#ifdef __LITTLE_ENDIAN - -// Definition FCP Command Frame -typedef struct _AVCCmdFrm -{ - // AV/C command frame - __u8 ctype : 4 ; // command type - __u8 cts : 4 ; // always 0x0 for AVC - __u8 suid : 3 ; // subunit ID - __u8 sutyp : 5 ; // subunit_typ - __u8 opcode : 8 ; // opcode - __u8 operand[509] ; // array of operands [1-507] - int length; //length of the command frame -} AVCCmdFrm ; - -// Definition FCP Response Frame -typedef struct _AVCRspFrm -{ - // AV/C response frame - __u8 resp : 4 ; // response type - __u8 cts : 4 ; // always 0x0 for AVC - __u8 suid : 3 ; // subunit ID - __u8 sutyp : 5 ; // subunit_typ - __u8 opcode : 8 ; // opcode - __u8 operand[509] ; // array of operands [1-507] - int length; //length of the response frame -} AVCRspFrm ; - -#else - -typedef struct _AVCCmdFrm -{ - __u8 cts:4; - __u8 ctype:4; - __u8 sutyp:5; - __u8 suid:3; - __u8 opcode; - __u8 operand[509]; - int length; -} AVCCmdFrm; - -typedef struct _AVCRspFrm -{ - __u8 cts:4; - __u8 resp:4; - __u8 sutyp:5; - __u8 suid:3; - __u8 opcode; - __u8 operand[509]; - int length; -} AVCRspFrm; - -#endif - -/************************************************************* - AVC command types (ctype) -**************************************************************/// -#define CONTROL 0x00 -#define STATUS 0x01 -#define INQUIRY 0x02 -#define NOTIFY 0x03 - -/************************************************************* - AVC respond types -**************************************************************/// -#define NOT_IMPLEMENTED 0x8 -#define ACCEPTED 0x9 -#define REJECTED 0xA -#define STABLE 0xC -#define CHANGED 0xD -#define INTERIM 0xF - -/************************************************************* - AVC opcodes -**************************************************************/// -#define CONNECT 0x24 -#define DISCONNECT 0x25 -#define UNIT_INFO 0x30 -#define SUBUNIT_Info 0x31 -#define VENDOR 0x00 - -#define PLUG_INFO 0x02 -#define OPEN_DESCRIPTOR 0x08 -#define READ_DESCRIPTOR 0x09 -#define OBJECT_NUMBER_SELECT 0x0D - -/************************************************************* - AVCTuner opcodes -**************************************************************/ - -#define DSIT 0xC8 -#define DSD 0xCB -#define DESCRIPTOR_TUNER_STATUS 0x80 -#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 - -/************************************************************* - AVCTuner list types -**************************************************************/ -#define Multiplex_List 0x80 -#define Service_List 0x82 - -/************************************************************* - AVCTuner object entries -**************************************************************/ -#define Multiplex 0x80 -#define Service 0x82 -#define Service_with_specified_components 0x83 -#define Preferred_components 0x90 -#define Component 0x84 - -/************************************************************* - Vendor-specific commands -**************************************************************/ - -// digital everywhere vendor ID -#define SFE_VENDOR_DE_COMPANYID_0 0x00 -#define SFE_VENDOR_DE_COMPANYID_1 0x12 -#define SFE_VENDOR_DE_COMPANYID_2 0x87 - -#define SFE_VENDOR_MAX_NR_COMPONENTS 0x4 -#define SFE_VENDOR_MAX_NR_SERVICES 0x3 -#define SFE_VENDOR_MAX_NR_DSD_ELEMENTS 0x10 - -// vendor commands -#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0A -#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 -#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 // QPSK command for DVB-S - -// TODO: following vendor specific commands needs to be implemented -#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 -#define SFE_VENDOR_OPCODE_HOST2CA 0x56 -#define SFE_VENDOR_OPCODE_CA2HOST 0x57 -#define SFE_VENDOR_OPCODE_CISTATUS 0x59 -#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 // QPSK command for DVB-S2 devices - -// CA Tags -#define SFE_VENDOR_TAG_CA_RESET 0x00 -#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 -#define SFE_VENDOR_TAG_CA_PMT 0x02 -#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 -#define SFE_VENDOR_TAG_CA_MMI 0x05 -#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 - - -//AVCTuner DVB identifier service_ID -#define DVB 0x20 - -/************************************************************* - AVC descriptor types -**************************************************************/ - -#define Subunit_Identifier_Descriptor 0x00 -#define Tuner_Status_Descriptor 0x80 - -typedef struct { - __u8 Subunit_Type; - __u8 Max_Subunit_ID; -} SUBUNIT_INFO; - -/************************************************************* - - AVCTuner DVB object IDs are 6 byte long - -**************************************************************/ - -typedef struct { - __u8 Byte0; - __u8 Byte1; - __u8 Byte2; - __u8 Byte3; - __u8 Byte4; - __u8 Byte5; -}OBJECT_ID; - -/************************************************************* - MULIPLEX Structs -**************************************************************/ -typedef struct -{ -#ifdef __LITTLE_ENDIAN - __u8 RF_frequency_hByte:6; - __u8 raster_Frequency:2;//Bit7,6 raster frequency -#else - __u8 raster_Frequency:2; - __u8 RF_frequency_hByte:6; -#endif - __u8 RF_frequency_mByte; - __u8 RF_frequency_lByte; - -}FREQUENCY; - -#ifdef __LITTLE_ENDIAN - -typedef struct -{ - __u8 Modulation :1; - __u8 FEC_inner :1; - __u8 FEC_outer :1; - __u8 Symbol_Rate :1; - __u8 Frequency :1; - __u8 Orbital_Pos :1; - __u8 Polarisation :1; - __u8 reserved_fields :1; - __u8 reserved1 :7; - __u8 Network_ID :1; - -}MULTIPLEX_VALID_FLAGS; - -typedef struct -{ - __u8 GuardInterval:1; - __u8 CodeRateLPStream:1; - __u8 CodeRateHPStream:1; - __u8 HierarchyInfo:1; - __u8 Constellation:1; - __u8 Bandwidth:1; - __u8 CenterFrequency:1; - __u8 reserved1:1; - __u8 reserved2:5; - __u8 OtherFrequencyFlag:1; - __u8 TransmissionMode:1; - __u8 NetworkId:1; -}MULTIPLEX_VALID_FLAGS_DVBT; - -#else - -typedef struct { - __u8 reserved_fields:1; - __u8 Polarisation:1; - __u8 Orbital_Pos:1; - __u8 Frequency:1; - __u8 Symbol_Rate:1; - __u8 FEC_outer:1; - __u8 FEC_inner:1; - __u8 Modulation:1; - __u8 Network_ID:1; - __u8 reserved1:7; -}MULTIPLEX_VALID_FLAGS; - -typedef struct { - __u8 reserved1:1; - __u8 CenterFrequency:1; - __u8 Bandwidth:1; - __u8 Constellation:1; - __u8 HierarchyInfo:1; - __u8 CodeRateHPStream:1; - __u8 CodeRateLPStream:1; - __u8 GuardInterval:1; - __u8 NetworkId:1; - __u8 TransmissionMode:1; - __u8 OtherFrequencyFlag:1; - __u8 reserved2:5; -}MULTIPLEX_VALID_FLAGS_DVBT; - -#endif - -typedef union { - MULTIPLEX_VALID_FLAGS Bits; - MULTIPLEX_VALID_FLAGS_DVBT Bits_T; - struct { - __u8 ByteHi; - __u8 ByteLo; - } Valid_Word; -} M_VALID_FLAGS; - -typedef struct -{ -#ifdef __LITTLE_ENDIAN - __u8 ActiveSystem; - __u8 reserved:5; - __u8 NoRF:1; - __u8 Moving:1; - __u8 Searching:1; - - __u8 SelectedAntenna:7; - __u8 Input:1; - - __u8 BER[4]; - - __u8 SignalStrength; - FREQUENCY Frequency; - - __u8 ManDepInfoLength; - - __u8 PowerSupply:1; - __u8 FrontEndPowerStatus:1; - __u8 reserved3:1; - __u8 AntennaError:1; - __u8 FrontEndError:1; - __u8 reserved2:3; - - __u8 CarrierNoiseRatio[2]; - __u8 reserved4[2]; - __u8 PowerSupplyVoltage; - __u8 AntennaVoltage; - __u8 FirewireBusVoltage; - - __u8 CaMmi:1; - __u8 reserved5:7; - - __u8 reserved6:1; - __u8 CaInitializationStatus:1; - __u8 CaErrorFlag:1; - __u8 CaDvbFlag:1; - __u8 CaModulePresentStatus:1; - __u8 CaApplicationInfo:1; - __u8 CaDateTimeRequest:1; - __u8 CaPmtReply:1; - -#else - __u8 ActiveSystem; - __u8 Searching:1; - __u8 Moving:1; - __u8 NoRF:1; - __u8 reserved:5; - - __u8 Input:1; - __u8 SelectedAntenna:7; - - __u8 BER[4]; - - __u8 SignalStrength; - FREQUENCY Frequency; - - __u8 ManDepInfoLength; - - __u8 reserved2:3; - __u8 FrontEndError:1; - __u8 AntennaError:1; - __u8 reserved3:1; - __u8 FrontEndPowerStatus:1; - __u8 PowerSupply:1; - - __u8 CarrierNoiseRatio[2]; - __u8 reserved4[2]; - __u8 PowerSupplyVoltage; - __u8 AntennaVoltage; - __u8 FirewireBusVoltage; - - __u8 reserved5:7; - __u8 CaMmi:1; - __u8 CaPmtReply:1; - __u8 CaDateTimeRequest:1; - __u8 CaApplicationInfo:1; - __u8 CaModulePresentStatus:1; - __u8 CaDvbFlag:1; - __u8 CaErrorFlag:1; - __u8 CaInitializationStatus:1; - __u8 reserved6:1; - -#endif -} ANTENNA_INPUT_INFO; // 22 Byte - -#define LNBCONTROL_DONTCARE 0xff - -struct dvb_diseqc_master_cmd; -struct dvb_frontend_parameters; -struct firedtv; - -int avc_recv(struct firedtv *fdtv, u8 *data, size_t length); - -int AVCTuner_DSIT(struct firedtv *fdtv, int Source_Plug, - struct dvb_frontend_parameters *params, __u8 *status); - -int avc_tuner_status(struct firedtv *fdtv, - ANTENNA_INPUT_INFO *antenna_input_info); -int avc_tuner_dsd(struct firedtv *fdtv, - struct dvb_frontend_parameters *params); -int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]); -int avc_tuner_get_ts(struct firedtv *fdtv); -int avc_identify_subunit(struct firedtv *fdtv); -int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, - char conttone, char nrdiseq, - struct dvb_diseqc_master_cmd *diseqcmd); -void avc_remote_ctrl_work(struct work_struct *work); -int avc_register_remote_control(struct firedtv *fdtv); -int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len); -int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len); -int avc_ca_reset(struct firedtv *fdtv); -int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length); -int avc_ca_get_time_date(struct firedtv *fdtv, int *interval); -int avc_ca_enter_menu(struct firedtv *fdtv); -int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len); - -#endif /* _AVC_API_H */ diff --git a/drivers/media/dvb/firewire/cmp.c b/drivers/media/dvb/firewire/cmp.c deleted file mode 100644 index 821e033d8195..000000000000 --- a/drivers/media/dvb/firewire/cmp.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * FireDTV driver (formerly known as FireSAT) - * - * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/mutex.h> -#include <linux/types.h> - -#include <asm/byteorder.h> - -#include <ieee1394.h> -#include <nodemgr.h> - -#include "avc.h" -#include "cmp.h" -#include "firedtv.h" - -#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL - -static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) -{ - int ret; - - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; - - ret = hpsb_node_read(fdtv->ud->ne, addr, buf, len); - if (ret < 0) - dev_err(&fdtv->ud->device, "CMP: read I/O error\n"); - - mutex_unlock(&fdtv->avc_mutex); - return ret; -} - -static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg, - int ext_tcode) -{ - int ret; - - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; - - ret = hpsb_node_lock(fdtv->ud->ne, addr, ext_tcode, data, - (__force quadlet_t)arg); - if (ret < 0) - dev_err(&fdtv->ud->device, "CMP: lock I/O error\n"); - - mutex_unlock(&fdtv->avc_mutex); - return ret; -} - -static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) -{ - return (be32_to_cpu(opcr) >> shift) & mask; -} - -static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) -{ - *opcr &= ~cpu_to_be32(mask << shift); - *opcr |= cpu_to_be32((value & mask) << shift); -} - -#define get_opcr_online(v) get_opcr((v), 0x1, 31) -#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) -#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) - -#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) -#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) -#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) -#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) - -int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) -{ - __be32 old_opcr, opcr; - u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); - int attempts = 0; - int ret; - - ret = cmp_read(fdtv, &opcr, opcr_address, 4); - if (ret < 0) - return ret; - -repeat: - if (!get_opcr_online(opcr)) { - dev_err(&fdtv->ud->device, "CMP: output offline\n"); - return -EBUSY; - } - - old_opcr = opcr; - - if (get_opcr_p2p_connections(opcr)) { - if (get_opcr_channel(opcr) != channel) { - dev_err(&fdtv->ud->device, - "CMP: cannot change channel\n"); - return -EBUSY; - } - dev_info(&fdtv->ud->device, - "CMP: overlaying existing connection\n"); - - /* We don't allocate isochronous resources. */ - } else { - set_opcr_channel(&opcr, channel); - set_opcr_data_rate(&opcr, IEEE1394_SPEED_400); - - /* FIXME: this is for the worst case - optimize */ - set_opcr_overhead_id(&opcr, 0); - - /* FIXME: allocate isochronous channel and bandwidth at IRM */ - } - - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); - - ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr, 2); - if (ret < 0) - return ret; - - if (old_opcr != opcr) { - /* - * FIXME: if old_opcr.P2P_Connections > 0, - * deallocate isochronous channel and bandwidth at IRM - */ - - if (++attempts < 6) /* arbitrary limit */ - goto repeat; - return -EBUSY; - } - - return 0; -} - -void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) -{ - __be32 old_opcr, opcr; - u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); - int attempts = 0; - - if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0) - return; - -repeat: - if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || - get_opcr_channel(opcr) != channel) { - dev_err(&fdtv->ud->device, "CMP: no connection to break\n"); - return; - } - - old_opcr = opcr; - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); - - if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr, 2) < 0) - return; - - if (old_opcr != opcr) { - /* - * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last - * owner, deallocate isochronous channel and bandwidth at IRM - */ - - if (++attempts < 6) /* arbitrary limit */ - goto repeat; - } -} diff --git a/drivers/media/dvb/firewire/cmp.h b/drivers/media/dvb/firewire/cmp.h deleted file mode 100644 index 17e182cf29a9..000000000000 --- a/drivers/media/dvb/firewire/cmp.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _CMP_H -#define _CMP_H - -struct firedtv; - -int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel); -void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel); - -#endif /* _CMP_H */ diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 953618246e8e..4e207658c5d9 100644 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -15,162 +15,181 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/list.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/string.h> #include <linux/types.h> -#include <dmxdev.h> -#include <dvb_demux.h> -#include <dvb_frontend.h> -#include <dvbdev.h> - +#include <dma.h> #include <csr1212.h> #include <highlevel.h> #include <hosts.h> -#include <ieee1394_hotplug.h> +#include <ieee1394.h> +#include <iso.h> #include <nodemgr.h> -#include "avc.h" -#include "cmp.h" #include "firedtv.h" -#include "firedtv-ci.h" -#include "firedtv-rc.h" - -#define MATCH_FLAGS IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ - IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION -#define DIGITAL_EVERYWHERE_OUI 0x001287 - -static struct ieee1394_device_id fdtv_id_table[] = { - - { - /* FloppyDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000024, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FloppyDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000025, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FloppyDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000026, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000034, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000035, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000036, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { } -}; -MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); +static LIST_HEAD(node_list); +static DEFINE_SPINLOCK(node_list_lock); -/* list of all firedtv devices */ -LIST_HEAD(fdtv_list); -DEFINE_SPINLOCK(fdtv_list_lock); +#define FIREWIRE_HEADER_SIZE 4 +#define CIP_HEADER_SIZE 8 -static void fcp_request(struct hpsb_host *host, - int nodeid, - int direction, - int cts, - u8 *data, - size_t length) +static void rawiso_activity_cb(struct hpsb_iso *iso) { - struct firedtv *fdtv = NULL; - struct firedtv *fdtv_entry; + struct firedtv *f, *fdtv = NULL; + unsigned int i, num, packet; + unsigned char *buf; unsigned long flags; + int count; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (f->backend_data == iso) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); + + packet = iso->first_packet; + num = hpsb_iso_n_ready(iso); + + if (!fdtv) { + dev_err(fdtv->device, "received at unknown iso channel\n"); + goto out; + } - if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { - - spin_lock_irqsave(&fdtv_list_lock, flags); - list_for_each_entry(fdtv_entry,&fdtv_list,list) { - if (fdtv_entry->ud->ne->host == host && - fdtv_entry->ud->ne->nodeid == nodeid && - (fdtv_entry->subunit == (data[1]&0x7) || - (fdtv_entry->subunit == 0 && - (data[1]&0x7) == 0x7))) { - fdtv=fdtv_entry; - break; - } + for (i = 0; i < num; i++, packet = (packet + 1) % iso->buf_packets) { + buf = dma_region_i(&iso->data_buf, unsigned char, + iso->infos[packet].offset + CIP_HEADER_SIZE); + count = (iso->infos[packet].len - CIP_HEADER_SIZE) / + (188 + FIREWIRE_HEADER_SIZE); + + /* ignore empty packet */ + if (iso->infos[packet].len <= CIP_HEADER_SIZE) + continue; + + while (count--) { + if (buf[FIREWIRE_HEADER_SIZE] == 0x47) + dvb_dmx_swfilter_packets(&fdtv->demux, + &buf[FIREWIRE_HEADER_SIZE], 1); + else + dev_err(fdtv->device, + "skipping invalid packet\n"); + buf += 188 + FIREWIRE_HEADER_SIZE; } - spin_unlock_irqrestore(&fdtv_list_lock, flags); + } +out: + hpsb_iso_recv_release_packets(iso, num); +} + +static inline struct node_entry *node_of(struct firedtv *fdtv) +{ + return container_of(fdtv->device, struct unit_directory, device)->ne; +} + +static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg) +{ + return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data, + (__force quadlet_t)arg); +} + +static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_read(node_of(fdtv), addr, data, len); +} + +static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_write(node_of(fdtv), addr, data, len); +} + +#define FDTV_ISO_BUFFER_PACKETS 256 +#define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200) + +static int start_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle; + int ret; + + iso_handle = hpsb_iso_recv_init(node_of(fdtv)->host, + FDTV_ISO_BUFFER_SIZE, FDTV_ISO_BUFFER_PACKETS, + fdtv->isochannel, HPSB_ISO_DMA_DEFAULT, + -1, /* stat.config.irq_interval */ + rawiso_activity_cb); + if (iso_handle == NULL) { + dev_err(fdtv->device, "cannot initialize iso receive\n"); + return -ENOMEM; + } + fdtv->backend_data = iso_handle; + + ret = hpsb_iso_recv_start(iso_handle, -1, -1, 0); + if (ret != 0) { + dev_err(fdtv->device, "cannot start iso receive\n"); + hpsb_iso_shutdown(iso_handle); + fdtv->backend_data = NULL; + } + return ret; +} + +static void stop_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle = fdtv->backend_data; - if (fdtv) - avc_recv(fdtv, data, length); + if (iso_handle != NULL) { + hpsb_iso_stop(iso_handle); + hpsb_iso_shutdown(iso_handle); } + fdtv->backend_data = NULL; } -const char *fdtv_model_names[] = { - [FIREDTV_UNKNOWN] = "unknown type", - [FIREDTV_DVB_S] = "FireDTV S/CI", - [FIREDTV_DVB_C] = "FireDTV C/CI", - [FIREDTV_DVB_T] = "FireDTV T/CI", - [FIREDTV_DVB_S2] = "FireDTV S2 ", +static const struct firedtv_backend fdtv_1394_backend = { + .lock = node_lock, + .read = node_read, + .write = node_write, + .start_iso = start_iso, + .stop_iso = stop_iso, }; -static int fdtv_probe(struct device *dev) +static void fcp_request(struct hpsb_host *host, int nodeid, int direction, + int cts, u8 *data, size_t length) { - struct unit_directory *ud = - container_of(dev, struct unit_directory, device); - struct firedtv *fdtv; + struct firedtv *f, *fdtv = NULL; unsigned long flags; - int kv_len; - void *kv_str; - int i; - int err = -ENOMEM; + int su; - fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); - if (!fdtv) - return -ENOMEM; + if (length == 0 || (data[0] & 0xf0) != 0) + return; - dev->driver_data = fdtv; - fdtv->ud = ud; - fdtv->subunit = 0; - fdtv->isochannel = -1; - fdtv->tone = 0xff; - fdtv->voltage = 0xff; + su = data[1] & 0x7; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (node_of(f)->host == host && + node_of(f)->nodeid == nodeid && + (f->subunit == su || (f->subunit == 0 && su == 0x7))) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); - mutex_init(&fdtv->avc_mutex); - init_waitqueue_head(&fdtv->avc_wait); - fdtv->avc_reply_received = true; - mutex_init(&fdtv->demux_mutex); - INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); + if (fdtv) + avc_recv(fdtv, data, length); +} + +static int node_probe(struct device *dev) +{ + struct unit_directory *ud = + container_of(dev, struct unit_directory, device); + struct firedtv *fdtv; + int kv_len, err; + void *kv_str; - /* Reading device model from ROM */ kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); - for (i = ARRAY_SIZE(fdtv_model_names); --i;) - if (strlen(fdtv_model_names[i]) <= kv_len && - strncmp(kv_str, fdtv_model_names[i], kv_len) == 0) - break; - fdtv->type = i; + + fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len); + if (!fdtv) + return -ENOMEM; /* * Work around a bug in udev's path_id script: Use the fw-host's dev @@ -180,50 +199,39 @@ static int fdtv_probe(struct device *dev) if (err) goto fail_free; - INIT_LIST_HEAD(&fdtv->list); - spin_lock_irqsave(&fdtv_list_lock, flags); - list_add_tail(&fdtv->list, &fdtv_list); - spin_unlock_irqrestore(&fdtv_list_lock, flags); + spin_lock_irq(&node_list_lock); + list_add_tail(&fdtv->list, &node_list); + spin_unlock_irq(&node_list_lock); err = avc_identify_subunit(fdtv); if (err) goto fail; - err = fdtv_dvbdev_init(fdtv, dev); + err = fdtv_dvb_register(fdtv); if (err) goto fail; avc_register_remote_control(fdtv); return 0; - fail: - spin_lock_irqsave(&fdtv_list_lock, flags); + spin_lock_irq(&node_list_lock); list_del(&fdtv->list); - spin_unlock_irqrestore(&fdtv_list_lock, flags); + spin_unlock_irq(&node_list_lock); fdtv_unregister_rc(fdtv); fail_free: kfree(fdtv); return err; } -static int fdtv_remove(struct device *dev) +static int node_remove(struct device *dev) { struct firedtv *fdtv = dev->driver_data; - unsigned long flags; - fdtv_ca_release(fdtv); - dvb_unregister_frontend(&fdtv->fe); - dvb_net_release(&fdtv->dvbnet); - fdtv->demux.dmx.close(&fdtv->demux.dmx); - fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, - &fdtv->frontend); - dvb_dmxdev_release(&fdtv->dmxdev); - dvb_dmx_release(&fdtv->demux); - dvb_unregister_adapter(&fdtv->adapter); - - spin_lock_irqsave(&fdtv_list_lock, flags); + fdtv_dvb_unregister(fdtv); + + spin_lock_irq(&node_list_lock); list_del(&fdtv->list); - spin_unlock_irqrestore(&fdtv_list_lock, flags); + spin_unlock_irq(&node_list_lock); cancel_work_sync(&fdtv->remote_ctrl_work); fdtv_unregister_rc(fdtv); @@ -232,7 +240,7 @@ static int fdtv_remove(struct device *dev) return 0; } -static int fdtv_update(struct unit_directory *ud) +static int node_update(struct unit_directory *ud) { struct firedtv *fdtv = ud->device.driver_data; @@ -243,17 +251,11 @@ static int fdtv_update(struct unit_directory *ud) } static struct hpsb_protocol_driver fdtv_driver = { - .name = "firedtv", - .id_table = fdtv_id_table, - .update = fdtv_update, - + .update = node_update, .driver = { - //.name and .bus are filled in for us in more recent linux versions - //.name = "FireDTV", - //.bus = &ieee1394_bus_type, - .probe = fdtv_probe, - .remove = fdtv_remove, + .probe = node_probe, + .remove = node_remove, }, }; @@ -262,11 +264,12 @@ static struct hpsb_highlevel fdtv_highlevel = { .fcp_request = fcp_request, }; -static int __init fdtv_init(void) +int __init fdtv_1394_init(struct ieee1394_device_id id_table[]) { int ret; hpsb_register_highlevel(&fdtv_highlevel); + fdtv_driver.id_table = id_table; ret = hpsb_register_protocol(&fdtv_driver); if (ret) { printk(KERN_ERR "firedtv: failed to register protocol\n"); @@ -275,17 +278,8 @@ static int __init fdtv_init(void) return ret; } -static void __exit fdtv_exit(void) +void __exit fdtv_1394_exit(void) { hpsb_unregister_protocol(&fdtv_driver); hpsb_unregister_highlevel(&fdtv_highlevel); } - -module_init(fdtv_init); -module_exit(fdtv_exit); - -MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); -MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); -MODULE_DESCRIPTION("FireDTV DVB Driver"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c new file mode 100644 index 000000000000..b55d9ccaf33e --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -0,0 +1,1315 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/bug.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/stringify.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include "firedtv.h" + +#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL + +#define AVC_CTYPE_CONTROL 0x0 +#define AVC_CTYPE_STATUS 0x1 +#define AVC_CTYPE_NOTIFY 0x3 + +#define AVC_RESPONSE_ACCEPTED 0x9 +#define AVC_RESPONSE_STABLE 0xc +#define AVC_RESPONSE_CHANGED 0xd +#define AVC_RESPONSE_INTERIM 0xf + +#define AVC_SUBUNIT_TYPE_TUNER (0x05 << 3) +#define AVC_SUBUNIT_TYPE_UNIT (0x1f << 3) + +#define AVC_OPCODE_VENDOR 0x00 +#define AVC_OPCODE_READ_DESCRIPTOR 0x09 +#define AVC_OPCODE_DSIT 0xc8 +#define AVC_OPCODE_DSD 0xcb + +#define DESCRIPTOR_TUNER_STATUS 0x80 +#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 + +#define SFE_VENDOR_DE_COMPANYID_0 0x00 /* OUI of Digital Everywhere */ +#define SFE_VENDOR_DE_COMPANYID_1 0x12 +#define SFE_VENDOR_DE_COMPANYID_2 0x87 + +#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0a +#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 +#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 /* for DVB-S */ + +#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 +#define SFE_VENDOR_OPCODE_HOST2CA 0x56 +#define SFE_VENDOR_OPCODE_CA2HOST 0x57 +#define SFE_VENDOR_OPCODE_CISTATUS 0x59 +#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 /* for DVB-S2 */ + +#define SFE_VENDOR_TAG_CA_RESET 0x00 +#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 +#define SFE_VENDOR_TAG_CA_PMT 0x02 +#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 +#define SFE_VENDOR_TAG_CA_MMI 0x05 +#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 + +#define EN50221_LIST_MANAGEMENT_ONLY 0x03 +#define EN50221_TAG_APP_INFO 0x9f8021 +#define EN50221_TAG_CA_INFO 0x9f8031 + +struct avc_command_frame { + int length; + u8 ctype; + u8 subunit; + u8 opcode; + u8 operand[509]; +}; + +struct avc_response_frame { + int length; + u8 response; + u8 subunit; + u8 opcode; + u8 operand[509]; +}; + +#define AVC_DEBUG_FCP_SUBACTIONS 1 +#define AVC_DEBUG_FCP_PAYLOADS 2 + +static int avc_debug; +module_param_named(debug, avc_debug, int, 0644); +MODULE_PARM_DESC(debug, "Verbose logging (default = 0" + ", FCP subactions = " __stringify(AVC_DEBUG_FCP_SUBACTIONS) + ", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS) + ", or all = -1)"); + +static const char *debug_fcp_ctype(unsigned int ctype) +{ + static const char *ctypes[] = { + [0x0] = "CONTROL", [0x1] = "STATUS", + [0x2] = "SPECIFIC INQUIRY", [0x3] = "NOTIFY", + [0x4] = "GENERAL INQUIRY", [0x8] = "NOT IMPLEMENTED", + [0x9] = "ACCEPTED", [0xa] = "REJECTED", + [0xb] = "IN TRANSITION", [0xc] = "IMPLEMENTED/STABLE", + [0xd] = "CHANGED", [0xf] = "INTERIM", + }; + const char *ret = ctype < ARRAY_SIZE(ctypes) ? ctypes[ctype] : NULL; + + return ret ? ret : "?"; +} + +static const char *debug_fcp_opcode(unsigned int opcode, + const u8 *data, size_t length) +{ + switch (opcode) { + case AVC_OPCODE_VENDOR: break; + case AVC_OPCODE_READ_DESCRIPTOR: return "ReadDescriptor"; + case AVC_OPCODE_DSIT: return "DirectSelectInfo.Type"; + case AVC_OPCODE_DSD: return "DirectSelectData"; + default: return "?"; + } + + if (length < 7 || + data[3] != SFE_VENDOR_DE_COMPANYID_0 || + data[4] != SFE_VENDOR_DE_COMPANYID_1 || + data[5] != SFE_VENDOR_DE_COMPANYID_2) + return "Vendor"; + + switch (data[6]) { + case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: return "RegisterRC"; + case SFE_VENDOR_OPCODE_LNB_CONTROL: return "LNBControl"; + case SFE_VENDOR_OPCODE_TUNE_QPSK: return "TuneQPSK"; + case SFE_VENDOR_OPCODE_HOST2CA: return "Host2CA"; + case SFE_VENDOR_OPCODE_CA2HOST: return "CA2Host"; + } + return "Vendor"; +} + +static void debug_fcp(const u8 *data, size_t length) +{ + unsigned int subunit_type, subunit_id, op; + const char *prefix = data[0] > 7 ? "FCP <- " : "FCP -> "; + + if (avc_debug & AVC_DEBUG_FCP_SUBACTIONS) { + subunit_type = data[1] >> 3; + subunit_id = data[1] & 7; + op = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2]; + printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n", + prefix, subunit_type, subunit_id, length, + debug_fcp_ctype(data[0]), + debug_fcp_opcode(op, data, length)); + } + + if (avc_debug & AVC_DEBUG_FCP_PAYLOADS) + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, 16, 1, + data, length, false); +} + +static int __avc_write(struct firedtv *fdtv, + const struct avc_command_frame *c, struct avc_response_frame *r) +{ + int err, retry; + + if (r) + fdtv->avc_reply_received = false; + + for (retry = 0; retry < 6; retry++) { + if (unlikely(avc_debug)) + debug_fcp(&c->ctype, c->length); + + err = fdtv->backend->write(fdtv, FCP_COMMAND_REGISTER, + (void *)&c->ctype, c->length); + if (err) { + fdtv->avc_reply_received = true; + dev_err(fdtv->device, "FCP command write failed\n"); + return err; + } + + if (!r) + return 0; + + /* + * AV/C specs say that answers should be sent within 150 ms. + * Time out after 200 ms. + */ + if (wait_event_timeout(fdtv->avc_wait, + fdtv->avc_reply_received, + msecs_to_jiffies(200)) != 0) { + r->length = fdtv->response_length; + memcpy(&r->response, fdtv->response, r->length); + + return 0; + } + } + dev_err(fdtv->device, "FCP response timed out\n"); + return -ETIMEDOUT; +} + +static int avc_write(struct firedtv *fdtv, + const struct avc_command_frame *c, struct avc_response_frame *r) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = __avc_write(fdtv, c, r); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +int avc_recv(struct firedtv *fdtv, void *data, size_t length) +{ + struct avc_response_frame *r = + data - offsetof(struct avc_response_frame, response); + + if (unlikely(avc_debug)) + debug_fcp(data, length); + + if (length >= 8 && + r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && + r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && + r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && + r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { + if (r->response == AVC_RESPONSE_CHANGED) { + fdtv_handle_rc(fdtv, + r->operand[4] << 8 | r->operand[5]); + schedule_work(&fdtv->remote_ctrl_work); + } else if (r->response != AVC_RESPONSE_INTERIM) { + dev_info(fdtv->device, + "remote control result = %d\n", r->response); + } + return 0; + } + + if (fdtv->avc_reply_received) { + dev_err(fdtv->device, "out-of-order AVC response, ignored\n"); + return -EIO; + } + + memcpy(fdtv->response, data, length); + fdtv->response_length = length; + + fdtv->avc_reply_received = true; + wake_up(&fdtv->avc_wait); + + return 0; +} + +/* + * tuning command for setting the relative LNB frequency + * (not supported by the AVC standard) + */ +static void avc_tuner_tuneqpsk(struct firedtv *fdtv, + struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; + + c->operand[4] = (params->frequency >> 24) & 0xff; + c->operand[5] = (params->frequency >> 16) & 0xff; + c->operand[6] = (params->frequency >> 8) & 0xff; + c->operand[7] = params->frequency & 0xff; + + c->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; + c->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: c->operand[10] = 0x1; break; + case FEC_2_3: c->operand[10] = 0x2; break; + case FEC_3_4: c->operand[10] = 0x3; break; + case FEC_5_6: c->operand[10] = 0x4; break; + case FEC_7_8: c->operand[10] = 0x5; break; + case FEC_4_5: + case FEC_8_9: + case FEC_AUTO: + default: c->operand[10] = 0x0; + } + + if (fdtv->voltage == 0xff) + c->operand[11] = 0xff; + else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ + c->operand[11] = 0; + else + c->operand[11] = 1; + + if (fdtv->tone == 0xff) + c->operand[12] = 0xff; + else if (fdtv->tone == SEC_TONE_ON) /* band */ + c->operand[12] = 1; + else + c->operand[12] = 0; + + if (fdtv->type == FIREDTV_DVB_S2) { + c->operand[13] = 0x1; + c->operand[14] = 0xff; + c->operand[15] = 0xff; + c->length = 20; + } else { + c->length = 16; + } +} + +static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x11; /* system_specific_multiplex selection_length */ + + /* multiplex_valid_flags, high byte */ + c->operand[5] = 0 << 7 /* reserved */ + | 0 << 6 /* Polarisation */ + | 0 << 5 /* Orbital_Pos */ + | 1 << 4 /* Frequency */ + | 1 << 3 /* Symbol_Rate */ + | 0 << 2 /* FEC_outer */ + | (params->u.qam.fec_inner != FEC_AUTO ? 1 << 1 : 0) + | (params->u.qam.modulation != QAM_AUTO ? 1 << 0 : 0); + + /* multiplex_valid_flags, low byte */ + c->operand[6] = 0 << 7 /* NetworkID */ + | 0 << 0 /* reserved */ ; + + c->operand[7] = 0x00; + c->operand[8] = 0x00; + c->operand[9] = 0x00; + c->operand[10] = 0x00; + + c->operand[11] = (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); + c->operand[12] = ((params->frequency / 4000) >> 8) & 0xff; + c->operand[13] = (params->frequency / 4000) & 0xff; + c->operand[14] = ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; + c->operand[15] = ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; + c->operand[16] = ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; + c->operand[17] = 0x00; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: c->operand[18] = 0x1; break; + case FEC_2_3: c->operand[18] = 0x2; break; + case FEC_3_4: c->operand[18] = 0x3; break; + case FEC_5_6: c->operand[18] = 0x4; break; + case FEC_7_8: c->operand[18] = 0x5; break; + case FEC_8_9: c->operand[18] = 0x6; break; + case FEC_4_5: c->operand[18] = 0x8; break; + case FEC_AUTO: + default: c->operand[18] = 0x0; + } + + switch (params->u.qam.modulation) { + case QAM_16: c->operand[19] = 0x08; break; + case QAM_32: c->operand[19] = 0x10; break; + case QAM_64: c->operand[19] = 0x18; break; + case QAM_128: c->operand[19] = 0x20; break; + case QAM_256: c->operand[19] = 0x28; break; + case QAM_AUTO: + default: c->operand[19] = 0x00; + } + + c->operand[20] = 0x00; + c->operand[21] = 0x00; + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + c->operand[22] = 0x00; + + c->length = 28; +} + +static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + struct dvb_ofdm_parameters *ofdm = ¶ms->u.ofdm; + + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x0c; /* system_specific_multiplex selection_length */ + + /* multiplex_valid_flags, high byte */ + c->operand[5] = + 0 << 7 /* reserved */ + | 1 << 6 /* CenterFrequency */ + | (ofdm->bandwidth != BANDWIDTH_AUTO ? 1 << 5 : 0) + | (ofdm->constellation != QAM_AUTO ? 1 << 4 : 0) + | (ofdm->hierarchy_information != HIERARCHY_AUTO ? 1 << 3 : 0) + | (ofdm->code_rate_HP != FEC_AUTO ? 1 << 2 : 0) + | (ofdm->code_rate_LP != FEC_AUTO ? 1 << 1 : 0) + | (ofdm->guard_interval != GUARD_INTERVAL_AUTO ? 1 << 0 : 0); + + /* multiplex_valid_flags, low byte */ + c->operand[6] = + 0 << 7 /* NetworkID */ + | (ofdm->transmission_mode != TRANSMISSION_MODE_AUTO ? 1 << 6 : 0) + | 0 << 5 /* OtherFrequencyFlag */ + | 0 << 0 /* reserved */ ; + + c->operand[7] = 0x0; + c->operand[8] = (params->frequency / 10) >> 24; + c->operand[9] = ((params->frequency / 10) >> 16) & 0xff; + c->operand[10] = ((params->frequency / 10) >> 8) & 0xff; + c->operand[11] = (params->frequency / 10) & 0xff; + + switch (ofdm->bandwidth) { + case BANDWIDTH_7_MHZ: c->operand[12] = 0x20; break; + case BANDWIDTH_8_MHZ: + case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ + case BANDWIDTH_AUTO: + default: c->operand[12] = 0x00; + } + + switch (ofdm->constellation) { + case QAM_16: c->operand[13] = 1 << 6; break; + case QAM_64: c->operand[13] = 2 << 6; break; + case QPSK: + default: c->operand[13] = 0x00; + } + + switch (ofdm->hierarchy_information) { + case HIERARCHY_1: c->operand[13] |= 1 << 3; break; + case HIERARCHY_2: c->operand[13] |= 2 << 3; break; + case HIERARCHY_4: c->operand[13] |= 3 << 3; break; + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + default: break; + } + + switch (ofdm->code_rate_HP) { + case FEC_2_3: c->operand[13] |= 1; break; + case FEC_3_4: c->operand[13] |= 2; break; + case FEC_5_6: c->operand[13] |= 3; break; + case FEC_7_8: c->operand[13] |= 4; break; + case FEC_1_2: + default: break; + } + + switch (ofdm->code_rate_LP) { + case FEC_2_3: c->operand[14] = 1 << 5; break; + case FEC_3_4: c->operand[14] = 2 << 5; break; + case FEC_5_6: c->operand[14] = 3 << 5; break; + case FEC_7_8: c->operand[14] = 4 << 5; break; + case FEC_1_2: + default: c->operand[14] = 0x00; break; + } + + switch (ofdm->guard_interval) { + case GUARD_INTERVAL_1_16: c->operand[14] |= 1 << 3; break; + case GUARD_INTERVAL_1_8: c->operand[14] |= 2 << 3; break; + case GUARD_INTERVAL_1_4: c->operand[14] |= 3 << 3; break; + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + default: break; + } + + switch (ofdm->transmission_mode) { + case TRANSMISSION_MODE_8K: c->operand[14] |= 1 << 1; break; + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + default: break; + } + + c->operand[15] = 0x00; /* network_ID[0] */ + c->operand[16] = 0x00; /* network_ID[1] */ + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + c->operand[17] = 0x00; + + c->length = 24; +} + +int avc_tuner_dsd(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + + switch (fdtv->type) { + case FIREDTV_DVB_S: + case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break; + case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(params, c); break; + case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(params, c); break; + default: + BUG(); + } + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(500); +#if 0 + /* FIXME: */ + /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ + if (status) + *status = r->operand[2]; +#endif + return 0; +} + +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + int pos, k; + + if (pidc > 16 && pidc != 0xff) + return -EINVAL; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x00; /* system_specific_multiplex selection_length */ + c->operand[5] = pidc; /* Nr_of_dsd_sel_specs */ + + pos = 6; + if (pidc != 0xff) + for (k = 0; k < pidc; k++) { + c->operand[pos++] = 0x13; /* flowfunction relay */ + c->operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ + c->operand[pos++] = (pid[k] >> 8) & 0x1f; + c->operand[pos++] = pid[k] & 0xff; + c->operand[pos++] = 0x00; /* tableID */ + c->operand[pos++] = 0x00; /* filter_length */ + } + + c->length = ALIGN(3 + pos, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(50); + return 0; +} + +int avc_tuner_get_ts(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + int sl; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_DSIT; + + sl = fdtv->type == FIREDTV_DVB_T ? 0x0c : 0x11; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0xff; /* status */ + c->operand[3] = 0x20; /* system id = DVB */ + c->operand[4] = 0x00; /* antenna number */ + c->operand[5] = 0x0; /* system_specific_search_flags */ + c->operand[6] = sl; /* system_specific_multiplex selection_length */ + c->operand[7] = 0x00; /* valid_flags [0] */ + c->operand[8] = 0x00; /* valid_flags [1] */ + c->operand[7 + sl] = 0x00; /* nr_of_dsit_sel_specs (always 0) */ + + c->length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(250); + return 0; +} + +int avc_identify_subunit(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_READ_DESCRIPTOR; + + c->operand[0] = DESCRIPTOR_SUBUNIT_IDENTIFIER; + c->operand[1] = 0xff; + c->operand[2] = 0x00; + c->operand[3] = 0x00; /* length highbyte */ + c->operand[4] = 0x08; /* length lowbyte */ + c->operand[5] = 0x00; /* offset highbyte */ + c->operand[6] = 0x0d; /* offset lowbyte */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if ((r->response != AVC_RESPONSE_STABLE && + r->response != AVC_RESPONSE_ACCEPTED) || + (r->operand[3] << 8) + r->operand[4] != 8) { + dev_err(fdtv->device, "cannot read subunit identifier\n"); + return -EINVAL; + } + return 0; +} + +#define SIZEOF_ANTENNA_INPUT_INFO 22 + +int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int length; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_READ_DESCRIPTOR; + + c->operand[0] = DESCRIPTOR_TUNER_STATUS; + c->operand[1] = 0xff; /* read_result_status */ + c->operand[2] = 0x00; /* reserved */ + c->operand[3] = 0; /* SIZEOF_ANTENNA_INPUT_INFO >> 8; */ + c->operand[4] = 0; /* SIZEOF_ANTENNA_INPUT_INFO & 0xff; */ + c->operand[5] = 0x00; + c->operand[6] = 0x00; + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_STABLE && + r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, "cannot read tuner status\n"); + return -EINVAL; + } + + length = r->operand[9]; + if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) { + dev_err(fdtv->device, "got invalid tuner status\n"); + return -EINVAL; + } + + stat->active_system = r->operand[10]; + stat->searching = r->operand[11] >> 7 & 1; + stat->moving = r->operand[11] >> 6 & 1; + stat->no_rf = r->operand[11] >> 5 & 1; + stat->input = r->operand[12] >> 7 & 1; + stat->selected_antenna = r->operand[12] & 0x7f; + stat->ber = r->operand[13] << 24 | + r->operand[14] << 16 | + r->operand[15] << 8 | + r->operand[16]; + stat->signal_strength = r->operand[17]; + stat->raster_frequency = r->operand[18] >> 6 & 2; + stat->rf_frequency = (r->operand[18] & 0x3f) << 16 | + r->operand[19] << 8 | + r->operand[20]; + stat->man_dep_info_length = r->operand[21]; + stat->front_end_error = r->operand[22] >> 4 & 1; + stat->antenna_error = r->operand[22] >> 3 & 1; + stat->front_end_power_status = r->operand[22] >> 1 & 1; + stat->power_supply = r->operand[22] & 1; + stat->carrier_noise_ratio = r->operand[23] << 8 | + r->operand[24]; + stat->power_supply_voltage = r->operand[27]; + stat->antenna_voltage = r->operand[28]; + stat->firewire_bus_voltage = r->operand[29]; + stat->ca_mmi = r->operand[30] & 1; + stat->ca_pmt_reply = r->operand[31] >> 7 & 1; + stat->ca_date_time_request = r->operand[31] >> 6 & 1; + stat->ca_application_info = r->operand[31] >> 5 & 1; + stat->ca_module_present_status = r->operand[31] >> 4 & 1; + stat->ca_dvb_flag = r->operand[31] >> 3 & 1; + stat->ca_error_flag = r->operand[31] >> 2 & 1; + stat->ca_initialization_status = r->operand[31] >> 1 & 1; + + return 0; +} + +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int i, j, k; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL; + + c->operand[4] = voltage; + c->operand[5] = nrdiseq; + + i = 6; + + for (j = 0; j < nrdiseq; j++) { + c->operand[i++] = diseqcmd[j].msg_len; + + for (k = 0; k < diseqcmd[j].msg_len; k++) + c->operand[i++] = diseqcmd[j].msg[k]; + } + + c->operand[i++] = burst; + c->operand[i++] = conttone; + + c->length = ALIGN(3 + i, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, "LNB control failed\n"); + return -EINVAL; + } + + return 0; +} + +int avc_register_remote_control(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_NOTIFY; + c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; + + c->length = 8; + + return avc_write(fdtv, c, NULL); +} + +void avc_remote_ctrl_work(struct work_struct *work) +{ + struct firedtv *fdtv = + container_of(work, struct firedtv, remote_ctrl_work); + + /* Should it be rescheduled in failure cases? */ + avc_register_remote_control(fdtv); +} + +#if 0 /* FIXME: unused */ +int avc_tuner_host2ca(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} +#endif + +static int get_ca_object_pos(struct avc_response_frame *r) +{ + int length = 1; + + /* Check length of length field */ + if (r->operand[7] & 0x80) + length = (r->operand[7] & 0x7f) + 1; + return length + 7; +} + +static int get_ca_object_length(struct avc_response_frame *r) +{ +#if 0 /* FIXME: unused */ + int size = 0; + int i; + + if (r->operand[7] & 0x80) + for (i = 0; i < (r->operand[7] & 0x7f); i++) { + size <<= 8; + size += r->operand[8 + i]; + } +#endif + return r->operand[7]; +} + +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int pos; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + pos = get_ca_object_pos(r); + app_info[0] = (EN50221_TAG_APP_INFO >> 16) & 0xff; + app_info[1] = (EN50221_TAG_APP_INFO >> 8) & 0xff; + app_info[2] = (EN50221_TAG_APP_INFO >> 0) & 0xff; + app_info[3] = 6 + r->operand[pos + 4]; + app_info[4] = 0x01; + memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]); + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int pos; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + pos = get_ca_object_pos(r); + app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff; + app_info[1] = (EN50221_TAG_CA_INFO >> 8) & 0xff; + app_info[2] = (EN50221_TAG_CA_INFO >> 0) & 0xff; + app_info[3] = 2; + app_info[4] = r->operand[pos + 0]; + app_info[5] = r->operand[pos + 1]; + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_reset(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_RESET; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 1; /* length */ + c->operand[8] = 0; /* force hardware reset */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} + +int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int list_management; + int program_info_length; + int pmt_cmd_id; + int read_pos; + int write_pos; + int es_info_length; + int crc32_csum; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + if (msg[0] != EN50221_LIST_MANAGEMENT_ONLY) { + dev_info(fdtv->device, "forcing list_management to ONLY\n"); + msg[0] = EN50221_LIST_MANAGEMENT_ONLY; + } + /* We take the cmd_id from the programme level only! */ + list_management = msg[0]; + program_info_length = ((msg[4] & 0x0f) << 8) + msg[5]; + if (program_info_length > 0) + program_info_length--; /* Remove pmt_cmd_id */ + pmt_cmd_id = msg[6]; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_PMT; /* ca tag */ + c->operand[6] = 0; /* more/last */ + /* c->operand[7] = XXXprogram_info_length + 17; */ /* length */ + c->operand[8] = list_management; + c->operand[9] = 0x01; /* pmt_cmd=OK_descramble */ + + /* TS program map table */ + + c->operand[10] = 0x02; /* Table id=2 */ + c->operand[11] = 0x80; /* Section syntax + length */ + /* c->operand[12] = XXXprogram_info_length + 12; */ + c->operand[13] = msg[1]; /* Program number */ + c->operand[14] = msg[2]; + c->operand[15] = 0x01; /* Version number=0 + current/next=1 */ + c->operand[16] = 0x00; /* Section number=0 */ + c->operand[17] = 0x00; /* Last section number=0 */ + c->operand[18] = 0x1f; /* PCR_PID=1FFF */ + c->operand[19] = 0xff; + c->operand[20] = (program_info_length >> 8); /* Program info length */ + c->operand[21] = (program_info_length & 0xff); + + /* CA descriptors at programme level */ + read_pos = 6; + write_pos = 22; + if (program_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(fdtv->device, + "invalid pmt_cmd_id %d\n", pmt_cmd_id); + + memcpy(&c->operand[write_pos], &msg[read_pos], + program_info_length); + read_pos += program_info_length; + write_pos += program_info_length; + } + while (read_pos < length) { + c->operand[write_pos++] = msg[read_pos++]; + c->operand[write_pos++] = msg[read_pos++]; + c->operand[write_pos++] = msg[read_pos++]; + es_info_length = + ((msg[read_pos] & 0x0f) << 8) + msg[read_pos + 1]; + read_pos += 2; + if (es_info_length > 0) + es_info_length--; /* Remove pmt_cmd_id */ + c->operand[write_pos++] = es_info_length >> 8; + c->operand[write_pos++] = es_info_length & 0xff; + if (es_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(fdtv->device, "invalid pmt_cmd_id %d " + "at stream level\n", pmt_cmd_id); + + memcpy(&c->operand[write_pos], &msg[read_pos], + es_info_length); + read_pos += es_info_length; + write_pos += es_info_length; + } + } + + /* CRC */ + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + + c->operand[7] = write_pos - 8; + c->operand[12] = write_pos - 13; + + crc32_csum = crc32_be(0, &c->operand[10], c->operand[12] - 1); + c->operand[write_pos - 4] = (crc32_csum >> 24) & 0xff; + c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff; + c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff; + c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff; + + c->length = ALIGN(3 + write_pos, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, + "CA PMT failed with response 0x%x\n", r->response); + return -EFAULT; + } + + return 0; +} + +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *interval = r->operand[get_ca_object_pos(r)]; + + return 0; +} + +int avc_ca_enter_menu(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} + +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_MMI; + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *len = get_ca_object_length(r); + memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len); + + return 0; +} + +#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL + +static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = fdtv->backend->read(fdtv, addr, buf, len); + if (ret < 0) + dev_err(fdtv->device, "CMP: read I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = fdtv->backend->lock(fdtv, addr, data, arg); + if (ret < 0) + dev_err(fdtv->device, "CMP: lock I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) +{ + return (be32_to_cpu(opcr) >> shift) & mask; +} + +static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) +{ + *opcr &= ~cpu_to_be32(mask << shift); + *opcr |= cpu_to_be32((value & mask) << shift); +} + +#define get_opcr_online(v) get_opcr((v), 0x1, 31) +#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) +#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) + +#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) +#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) +#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) +#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) + +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + int ret; + + ret = cmp_read(fdtv, &opcr, opcr_address, 4); + if (ret < 0) + return ret; + +repeat: + if (!get_opcr_online(opcr)) { + dev_err(fdtv->device, "CMP: output offline\n"); + return -EBUSY; + } + + old_opcr = opcr; + + if (get_opcr_p2p_connections(opcr)) { + if (get_opcr_channel(opcr) != channel) { + dev_err(fdtv->device, "CMP: cannot change channel\n"); + return -EBUSY; + } + dev_info(fdtv->device, "CMP: overlaying connection\n"); + + /* We don't allocate isochronous resources. */ + } else { + set_opcr_channel(&opcr, channel); + set_opcr_data_rate(&opcr, 2); /* S400 */ + + /* FIXME: this is for the worst case - optimize */ + set_opcr_overhead_id(&opcr, 0); + + /* + * FIXME: allocate isochronous channel and bandwidth at IRM + * fdtv->backend->alloc_resources(fdtv, channels_mask, bw); + */ + } + + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); + + ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr); + if (ret < 0) + return ret; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections > 0, + * deallocate isochronous channel and bandwidth at IRM + * if (...) + * fdtv->backend->dealloc_resources(fdtv, channel, bw); + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + return -EBUSY; + } + + return 0; +} + +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + + if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0) + return; + +repeat: + if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || + get_opcr_channel(opcr) != channel) { + dev_err(fdtv->device, "CMP: no connection to break\n"); + return; + } + + old_opcr = opcr; + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); + + if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr) < 0) + return; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last + * owner, deallocate isochronous channel and bandwidth at IRM + * if (...) + * fdtv->backend->dealloc_resources(fdtv, channel, bw); + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + } +} diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/dvb/firewire/firedtv-ci.c index 6d87926b8bfe..eeb80d0ea3ff 100644 --- a/drivers/media/dvb/firewire/firedtv-ci.c +++ b/drivers/media/dvb/firewire/firedtv-ci.c @@ -10,33 +10,37 @@ * the License, or (at your option) any later version. */ +#include <linux/device.h> #include <linux/dvb/ca.h> #include <linux/fs.h> #include <linux/module.h> #include <dvbdev.h> -#include "avc.h" #include "firedtv.h" -#include "firedtv-ci.h" -static int fdtv_ca_ready(ANTENNA_INPUT_INFO *info) +#define EN50221_TAG_APP_INFO_ENQUIRY 0x9f8020 +#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030 +#define EN50221_TAG_CA_PMT 0x9f8032 +#define EN50221_TAG_ENTER_MENU 0x9f8022 + +static int fdtv_ca_ready(struct firedtv_tuner_status *stat) { - return info->CaInitializationStatus == 1 && - info->CaErrorFlag == 0 && - info->CaDvbFlag == 1 && - info->CaModulePresentStatus == 1; + return stat->ca_initialization_status == 1 && + stat->ca_error_flag == 0 && + stat->ca_dvb_flag == 1 && + stat->ca_module_present_status == 1; } -static int fdtv_get_ca_flags(ANTENNA_INPUT_INFO *info) +static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat) { int flags = 0; - if (info->CaModulePresentStatus == 1) + if (stat->ca_module_present_status == 1) flags |= CA_CI_MODULE_PRESENT; - if (info->CaInitializationStatus == 1 && - info->CaErrorFlag == 0 && - info->CaDvbFlag == 1) + if (stat->ca_initialization_status == 1 && + stat->ca_error_flag == 0 && + stat->ca_dvb_flag == 1) flags |= CA_CI_MODULE_READY; return flags; } @@ -59,17 +63,17 @@ static int fdtv_ca_get_caps(void *arg) static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) { - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; struct ca_slot_info *slot = arg; - if (avc_tuner_status(fdtv, &info)) + if (avc_tuner_status(fdtv, &stat)) return -EFAULT; if (slot->num != 0) return -EFAULT; slot->type = CA_CI; - slot->flags = fdtv_get_ca_flags(&info); + slot->flags = fdtv_get_ca_flags(&stat); return 0; } @@ -77,8 +81,7 @@ static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) { struct ca_msg *reply = arg; - return - avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; + return avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; } static int fdtv_ca_info(struct firedtv *fdtv, void *arg) @@ -92,30 +95,29 @@ static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) { struct ca_msg *reply = arg; - return - avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; + return avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; } static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) { - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; int err; switch (fdtv->ca_last_command) { - case TAG_APP_INFO_ENQUIRY: + case EN50221_TAG_APP_INFO_ENQUIRY: err = fdtv_ca_app_info(fdtv, arg); break; - case TAG_CA_INFO_ENQUIRY: + case EN50221_TAG_CA_INFO_ENQUIRY: err = fdtv_ca_info(fdtv, arg); break; default: - if (avc_tuner_status(fdtv, &info)) + if (avc_tuner_status(fdtv, &stat)) err = -EFAULT; - else if (info.CaMmi == 1) + else if (stat.ca_mmi == 1) err = fdtv_ca_get_mmi(fdtv, arg); else { - printk(KERN_INFO "%s: Unhandled message 0x%08X\n", - __func__, fdtv->ca_last_command); + dev_info(fdtv->device, "unhandled CA message 0x%08x\n", + fdtv->ca_last_command); err = -EFAULT; } } @@ -133,14 +135,13 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) data_pos = 4; if (msg->msg[3] & 0x80) { data_length = 0; - for (i = 0; i < (msg->msg[3] & 0x7F); i++) + for (i = 0; i < (msg->msg[3] & 0x7f); i++) data_length = (data_length << 8) + msg->msg[data_pos++]; } else { data_length = msg->msg[3]; } - return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? - -EFAULT : 0; + return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? -EFAULT : 0; } static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) @@ -152,23 +153,23 @@ static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) fdtv->ca_last_command = (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; switch (fdtv->ca_last_command) { - case TAG_CA_PMT: + case EN50221_TAG_CA_PMT: err = fdtv_ca_pmt(fdtv, arg); break; - case TAG_APP_INFO_ENQUIRY: + case EN50221_TAG_APP_INFO_ENQUIRY: /* handled in ca_get_msg */ err = 0; break; - case TAG_CA_INFO_ENQUIRY: + case EN50221_TAG_CA_INFO_ENQUIRY: /* handled in ca_get_msg */ err = 0; break; - case TAG_ENTER_MENU: + case EN50221_TAG_ENTER_MENU: err = avc_ca_enter_menu(fdtv); break; default: - printk(KERN_ERR "%s: Unhandled unknown message 0x%08X\n", - __func__, fdtv->ca_last_command); + dev_err(fdtv->device, "unhandled CA message 0x%08x\n", + fdtv->ca_last_command); err = -EFAULT; } return err; @@ -179,10 +180,10 @@ static int fdtv_ca_ioctl(struct inode *inode, struct file *file, { struct dvb_device *dvbdev = file->private_data; struct firedtv *fdtv = dvbdev->priv; - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; int err; - switch(cmd) { + switch (cmd) { case CA_RESET: err = fdtv_ca_reset(fdtv); break; @@ -199,13 +200,12 @@ static int fdtv_ca_ioctl(struct inode *inode, struct file *file, err = fdtv_ca_send_msg(fdtv, arg); break; default: - printk(KERN_INFO "%s: Unhandled ioctl, command: %u\n",__func__, - cmd); + dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); err = -EOPNOTSUPP; } /* FIXME Is this necessary? */ - avc_tuner_status(fdtv, &info); + avc_tuner_status(fdtv, &stat); return err; } @@ -233,22 +233,21 @@ static struct dvb_device fdtv_ca = { int fdtv_ca_register(struct firedtv *fdtv) { - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; int err; - if (avc_tuner_status(fdtv, &info)) + if (avc_tuner_status(fdtv, &stat)) return -EINVAL; - if (!fdtv_ca_ready(&info)) + if (!fdtv_ca_ready(&stat)) return -EFAULT; err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, &fdtv_ca, fdtv, DVB_DEVICE_CA); - if (info.CaApplicationInfo == 0) - printk(KERN_ERR "%s: CaApplicationInfo is not set.\n", - __func__); - if (info.CaDateTimeRequest == 1) + if (stat.ca_application_info == 0) + dev_err(fdtv->device, "CaApplicationInfo is not set\n"); + if (stat.ca_date_time_request == 1) avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); return err; diff --git a/drivers/media/dvb/firewire/firedtv-ci.h b/drivers/media/dvb/firewire/firedtv-ci.h deleted file mode 100644 index d6840f5dcbae..000000000000 --- a/drivers/media/dvb/firewire/firedtv-ci.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _FIREDTV_CI_H -#define _FIREDTV_CI_H - -struct firedtv; - -int fdtv_ca_register(struct firedtv *fdtv); -void fdtv_ca_release(struct firedtv *fdtv); - -#endif /* _FIREDTV_CI_H */ diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c index 1823058696f2..9d308dd32a5c 100644 --- a/drivers/media/dvb/firewire/firedtv-dvb.c +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -10,75 +10,55 @@ * the License, or (at your option) any later version. */ +#include <linux/bitops.h> +#include <linux/device.h> #include <linux/errno.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/string.h> #include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <dmxdev.h> #include <dvb_demux.h> -#include <dvb_frontend.h> #include <dvbdev.h> +#include <dvb_frontend.h> -#include "avc.h" #include "firedtv.h" -#include "firedtv-ci.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -static struct firedtv_channel *fdtv_channel_allocate(struct firedtv *fdtv) +static int alloc_channel(struct firedtv *fdtv) { - struct firedtv_channel *c = NULL; - int k; + int i; - if (mutex_lock_interruptible(&fdtv->demux_mutex)) - return NULL; - - for (k = 0; k < 16; k++) - if (!fdtv->channel[k].active) { - fdtv->channel[k].active = true; - c = &fdtv->channel[k]; + for (i = 0; i < 16; i++) + if (!__test_and_set_bit(i, &fdtv->channel_active)) break; - } - - mutex_unlock(&fdtv->demux_mutex); - return c; + return i; } -static int fdtv_channel_collect(struct firedtv *fdtv, int *pidc, u16 pid[]) +static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[]) { - int k, l = 0; - - if (mutex_lock_interruptible(&fdtv->demux_mutex)) - return -EINTR; - - for (k = 0; k < 16; k++) - if (fdtv->channel[k].active) - pid[l++] = fdtv->channel[k].pid; - - mutex_unlock(&fdtv->demux_mutex); + int i, n; - *pidc = l; - - return 0; + for (i = 0, n = 0; i < 16; i++) + if (test_bit(i, &fdtv->channel_active)) + pid[n++] = fdtv->channel_pid[i]; + *pidc = n; } -static int fdtv_channel_release(struct firedtv *fdtv, - struct firedtv_channel *channel) +static inline void dealloc_channel(struct firedtv *fdtv, int i) { - if (mutex_lock_interruptible(&fdtv->demux_mutex)) - return -EINTR; - - channel->active = false; - - mutex_unlock(&fdtv->demux_mutex); - return 0; + __clear_bit(i, &fdtv->channel_active); } int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) { - struct firedtv *fdtv = (struct firedtv*)dvbdmxfeed->demux->priv; - struct firedtv_channel *channel; - int pidc,k; + struct firedtv *fdtv = dvbdmxfeed->demux->priv; + int pidc, c, ret; u16 pids[16]; switch (dvbdmxfeed->type) { @@ -86,11 +66,14 @@ int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) case DMX_TYPE_SEC: break; default: - printk(KERN_ERR "%s: invalid type %u\n", - __func__, dvbdmxfeed->type); + dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n", + dvbdmxfeed->type); return -EINVAL; } + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + if (dvbdmxfeed->type == DMX_TYPE_TS) { switch (dvbdmxfeed->pes_type) { case DMX_TS_PES_VIDEO: @@ -98,75 +81,64 @@ int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) case DMX_TS_PES_TELETEXT: case DMX_TS_PES_PCR: case DMX_TS_PES_OTHER: - //Dirty fix to keep fdtv->channel pid-list up to date - for(k=0;k<16;k++){ - if (!fdtv->channel[k].active) - fdtv->channel[k].pid = - dvbdmxfeed->pid; - break; - } - channel = fdtv_channel_allocate(fdtv); + c = alloc_channel(fdtv); break; default: - printk(KERN_ERR "%s: invalid pes type %u\n", - __func__, dvbdmxfeed->pes_type); - return -EINVAL; + dev_err(fdtv->device, + "can't start dmx feed: invalid pes type %u\n", + dvbdmxfeed->pes_type); + ret = -EINVAL; + goto out; } } else { - channel = fdtv_channel_allocate(fdtv); + c = alloc_channel(fdtv); } - if (!channel) { - printk(KERN_ERR "%s: busy!\n", __func__); - return -EBUSY; + if (c > 15) { + dev_err(fdtv->device, "can't start dmx feed: busy\n"); + ret = -EBUSY; + goto out; } - dvbdmxfeed->priv = channel; - channel->pid = dvbdmxfeed->pid; - - if (fdtv_channel_collect(fdtv, &pidc, pids)) { - fdtv_channel_release(fdtv, channel); - printk(KERN_ERR "%s: could not collect pids!\n", __func__); - return -EINTR; - } + dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c; + fdtv->channel_pid[c] = dvbdmxfeed->pid; + collect_channels(fdtv, &pidc, pids); if (dvbdmxfeed->pid == 8192) { - k = avc_tuner_get_ts(fdtv); - if (k) { - fdtv_channel_release(fdtv, channel); - printk("%s: AVCTuner_GetTS failed with error %d\n", - __func__, k); - return k; + ret = avc_tuner_get_ts(fdtv); + if (ret) { + dealloc_channel(fdtv, c); + dev_err(fdtv->device, "can't get TS\n"); + goto out; } } else { - k = avc_tuner_set_pids(fdtv, pidc, pids); - if (k) { - fdtv_channel_release(fdtv, channel); - printk("%s: AVCTuner_SetPIDs failed with error %d\n", - __func__, k); - return k; + ret = avc_tuner_set_pids(fdtv, pidc, pids); + if (ret) { + dealloc_channel(fdtv, c); + dev_err(fdtv->device, "can't set PIDs\n"); + goto out; } } +out: + mutex_unlock(&fdtv->demux_mutex); - return 0; + return ret; } int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *demux = dvbdmxfeed->demux; - struct firedtv *fdtv = (struct firedtv*)demux->priv; - struct firedtv_channel *c = dvbdmxfeed->priv; - int k, l; + struct firedtv *fdtv = demux->priv; + int pidc, c, ret; u16 pids[16]; - if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) && - (demux->dmx.frontend->source != DMX_MEMORY_FE))) { + if (dvbdmxfeed->type == DMX_TYPE_TS && + !((dvbdmxfeed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE))) { if (dvbdmxfeed->ts_type & TS_DECODER) { - if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || - !demux->pesfilter[dvbdmxfeed->pes_type]) - + !demux->pesfilter[dvbdmxfeed->pes_type]) return -EINVAL; demux->pids[dvbdmxfeed->pes_type] |= 0x8000; @@ -174,38 +146,32 @@ int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) } if (!(dvbdmxfeed->ts_type & TS_DECODER && - dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) - + dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) return 0; } if (mutex_lock_interruptible(&fdtv->demux_mutex)) return -EINTR; - /* list except channel to be removed */ - for (k = 0, l = 0; k < 16; k++) - if (fdtv->channel[k].active) { - if (&fdtv->channel[k] != c) - pids[l++] = fdtv->channel[k].pid; - else - fdtv->channel[k].active = false; - } + c = (unsigned long)dvbdmxfeed->priv; + dealloc_channel(fdtv, c); + collect_channels(fdtv, &pidc, pids); - k = avc_tuner_set_pids(fdtv, l, pids); - if (!k) - c->active = false; + ret = avc_tuner_set_pids(fdtv, pidc, pids); mutex_unlock(&fdtv->demux_mutex); - return k; + + return ret; } -int fdtv_dvbdev_init(struct firedtv *fdtv, struct device *dev) +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +int fdtv_dvb_register(struct firedtv *fdtv) { int err; - err = DVB_REGISTER_ADAPTER(&fdtv->adapter, - fdtv_model_names[fdtv->type], - THIS_MODULE, dev, adapter_nr); + err = dvb_register_adapter(&fdtv->adapter, fdtv_model_names[fdtv->type], + THIS_MODULE, fdtv->device, adapter_nr); if (err < 0) goto fail_log; @@ -223,9 +189,9 @@ int fdtv_dvbdev_init(struct firedtv *fdtv, struct device *dev) if (err) goto fail_unreg_adapter; - fdtv->dmxdev.filternum = 16; - fdtv->dmxdev.demux = &fdtv->demux.dmx; - fdtv->dmxdev.capabilities = 0; + fdtv->dmxdev.filternum = 16; + fdtv->dmxdev.demux = &fdtv->demux.dmx; + fdtv->dmxdev.capabilities = 0; err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); if (err) @@ -233,13 +199,12 @@ int fdtv_dvbdev_init(struct firedtv *fdtv, struct device *dev) fdtv->frontend.source = DMX_FRONTEND_0; - err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, - &fdtv->frontend); + err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend); if (err) goto fail_dmxdev_release; err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, - &fdtv->frontend); + &fdtv->frontend); if (err) goto fail_rem_frontend; @@ -252,16 +217,15 @@ int fdtv_dvbdev_init(struct firedtv *fdtv, struct device *dev) err = fdtv_ca_register(fdtv); if (err) - dev_info(dev, "Conditional Access Module not enabled\n"); - + dev_info(fdtv->device, + "Conditional Access Module not enabled\n"); return 0; fail_net_release: dvb_net_release(&fdtv->dvbnet); fdtv->demux.dmx.close(&fdtv->demux.dmx); fail_rem_frontend: - fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, - &fdtv->frontend); + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); fail_dmxdev_release: dvb_dmxdev_release(&fdtv->dmxdev); fail_dmx_release: @@ -269,8 +233,132 @@ fail_dmx_release: fail_unreg_adapter: dvb_unregister_adapter(&fdtv->adapter); fail_log: - dev_err(dev, "DVB initialization failed\n"); + dev_err(fdtv->device, "DVB initialization failed\n"); return err; } +void fdtv_dvb_unregister(struct firedtv *fdtv) +{ + fdtv_ca_release(fdtv); + dvb_unregister_frontend(&fdtv->fe); + dvb_net_release(&fdtv->dvbnet); + fdtv->demux.dmx.close(&fdtv->demux.dmx); + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); + dvb_dmxdev_release(&fdtv->dmxdev); + dvb_dmx_release(&fdtv->demux); + dvb_unregister_adapter(&fdtv->adapter); +} + +const char *fdtv_model_names[] = { + [FIREDTV_UNKNOWN] = "unknown type", + [FIREDTV_DVB_S] = "FireDTV S/CI", + [FIREDTV_DVB_C] = "FireDTV C/CI", + [FIREDTV_DVB_T] = "FireDTV T/CI", + [FIREDTV_DVB_S2] = "FireDTV S2 ", +}; + +struct firedtv *fdtv_alloc(struct device *dev, + const struct firedtv_backend *backend, + const char *name, size_t name_len) +{ + struct firedtv *fdtv; + int i; + + fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); + if (!fdtv) + return NULL; + + dev->driver_data = fdtv; + fdtv->device = dev; + fdtv->isochannel = -1; + fdtv->voltage = 0xff; + fdtv->tone = 0xff; + fdtv->backend = backend; + + mutex_init(&fdtv->avc_mutex); + init_waitqueue_head(&fdtv->avc_wait); + fdtv->avc_reply_received = true; + mutex_init(&fdtv->demux_mutex); + INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); + + for (i = ARRAY_SIZE(fdtv_model_names); --i; ) + if (strlen(fdtv_model_names[i]) <= name_len && + strncmp(name, fdtv_model_names[i], name_len) == 0) + break; + fdtv->type = i; + + return fdtv; +} + +#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ + IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) + +#define DIGITAL_EVERYWHERE_OUI 0x001287 +#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d +#define AVC_SW_VERSION_ENTRY 0x010001 + +static struct ieee1394_device_id fdtv_id_table[] = { + { + /* FloppyDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000024, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000025, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000026, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000034, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000035, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000036, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, {} +}; +MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); + +static int __init fdtv_init(void) +{ + return fdtv_1394_init(fdtv_id_table); +} + +static void __exit fdtv_exit(void) +{ + fdtv_1394_exit(); +} + +module_init(fdtv_init); +module_exit(fdtv_exit); +MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); +MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); +MODULE_DESCRIPTION("FireDTV DVB Driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c index f8150f402bb6..9b9539c800f8 100644 --- a/drivers/media/dvb/firewire/firedtv-fe.c +++ b/drivers/media/dvb/firewire/firedtv-fe.c @@ -10,6 +10,7 @@ * the License, or (at your option) any later version. */ +#include <linux/device.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/string.h> @@ -17,8 +18,6 @@ #include <dvb_frontend.h> -#include "avc.h" -#include "cmp.h" #include "firedtv.h" static int fdtv_dvb_init(struct dvb_frontend *fe) @@ -32,35 +31,37 @@ static int fdtv_dvb_init(struct dvb_frontend *fe) err = cmp_establish_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); if (err) { - printk(KERN_ERR "Could not establish point to point " - "connection.\n"); + dev_err(fdtv->device, + "could not establish point to point connection\n"); return err; } - return setup_iso_channel(fdtv); + return fdtv->backend->start_iso(fdtv); } static int fdtv_sleep(struct dvb_frontend *fe) { struct firedtv *fdtv = fe->sec_priv; - tear_down_iso_channel(fdtv); + fdtv->backend->stop_iso(fdtv); cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); fdtv->isochannel = -1; return 0; } +#define LNBCONTROL_DONTCARE 0xff + static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe, - struct dvb_diseqc_master_cmd *cmd) + struct dvb_diseqc_master_cmd *cmd) { struct firedtv *fdtv = fe->sec_priv; - return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, - LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, 1, cmd); + return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, + LNBCONTROL_DONTCARE, 1, cmd); } static int fdtv_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t minicmd) + fe_sec_mini_cmd_t minicmd) { return 0; } @@ -74,7 +75,7 @@ static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) } static int fdtv_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + fe_sec_voltage_t voltage) { struct firedtv *fdtv = fe->sec_priv; @@ -85,12 +86,12 @@ static int fdtv_set_voltage(struct dvb_frontend *fe, static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct firedtv *fdtv = fe->sec_priv; - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; - if (avc_tuner_status(fdtv, &info)) + if (avc_tuner_status(fdtv, &stat)) return -EINVAL; - if (info.NoRF) + if (stat.no_rf) *status = 0; else *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | @@ -101,39 +102,37 @@ static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) static int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber) { struct firedtv *fdtv = fe->sec_priv; - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; - if (avc_tuner_status(fdtv, &info)) + if (avc_tuner_status(fdtv, &stat)) return -EINVAL; - *ber = info.BER[0] << 24 | info.BER[1] << 16 | - info.BER[2] << 8 | info.BER[3]; + *ber = stat.ber; return 0; } -static int fdtv_read_signal_strength (struct dvb_frontend *fe, u16 *strength) +static int fdtv_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct firedtv *fdtv = fe->sec_priv; - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; - if (avc_tuner_status(fdtv, &info)) + if (avc_tuner_status(fdtv, &stat)) return -EINVAL; - *strength = info.SignalStrength << 8; + *strength = stat.signal_strength << 8; return 0; } static int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr) { struct firedtv *fdtv = fe->sec_priv; - ANTENNA_INPUT_INFO info; + struct firedtv_tuner_status stat; - if (avc_tuner_status(fdtv, &info)) + if (avc_tuner_status(fdtv, &stat)) return -EINVAL; /* C/N[dB] = -10 * log10(snr / 65535) */ - *snr = (info.CarrierNoiseRatio[0] << 8) + info.CarrierNoiseRatio[1]; - *snr *= 257; + *snr = stat.carrier_noise_ratio * 257; return 0; } @@ -142,8 +141,10 @@ static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) return -EOPNOTSUPP; } +#define ACCEPTED 0x9 + static int fdtv_set_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) + struct dvb_frontend_parameters *params) { struct firedtv *fdtv = fe->sec_priv; @@ -155,7 +156,7 @@ static int fdtv_set_frontend(struct dvb_frontend *fe, } static int fdtv_get_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) + struct dvb_frontend_parameters *params) { return -EOPNOTSUPP; } @@ -235,8 +236,8 @@ void fdtv_frontend_init(struct firedtv *fdtv) break; default: - printk(KERN_ERR "FireDTV: no frontend for model type %d\n", - fdtv->type); + dev_err(fdtv->device, "no frontend for model type %d\n", + fdtv->type); } strcpy(fi->name, fdtv_model_names[fdtv->type]); diff --git a/drivers/media/dvb/firewire/firedtv-iso.c b/drivers/media/dvb/firewire/firedtv-iso.c deleted file mode 100644 index a72df228e7de..000000000000 --- a/drivers/media/dvb/firewire/firedtv-iso.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * FireSAT DVB driver - * - * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/spinlock.h> - -#include <dvb_demux.h> - -#include <dma.h> -#include <iso.h> -#include <nodemgr.h> - -#include "firedtv.h" - -static void rawiso_activity_cb(struct hpsb_iso *iso); - -void tear_down_iso_channel(struct firedtv *fdtv) -{ - if (fdtv->iso_handle != NULL) { - hpsb_iso_stop(fdtv->iso_handle); - hpsb_iso_shutdown(fdtv->iso_handle); - } - fdtv->iso_handle = NULL; -} - -int setup_iso_channel(struct firedtv *fdtv) -{ - int result; - fdtv->iso_handle = - hpsb_iso_recv_init(fdtv->ud->ne->host, - 256 * 200, //data_buf_size, - 256, //buf_packets, - fdtv->isochannel, - HPSB_ISO_DMA_DEFAULT, //dma_mode, - -1, //stat.config.irq_interval, - rawiso_activity_cb); - if (fdtv->iso_handle == NULL) { - printk(KERN_ERR "Cannot initialize iso receive.\n"); - return -EINVAL; - } - result = hpsb_iso_recv_start(fdtv->iso_handle, -1, -1, 0); - if (result != 0) { - printk(KERN_ERR "Cannot start iso receive.\n"); - return -EINVAL; - } - return 0; -} - -static void rawiso_activity_cb(struct hpsb_iso *iso) -{ - unsigned int num; - unsigned int i; - unsigned int packet; - unsigned long flags; - struct firedtv *fdtv = NULL; - struct firedtv *fdtv_iterator; - - spin_lock_irqsave(&fdtv_list_lock, flags); - list_for_each_entry(fdtv_iterator, &fdtv_list, list) { - if(fdtv_iterator->iso_handle == iso) { - fdtv = fdtv_iterator; - break; - } - } - spin_unlock_irqrestore(&fdtv_list_lock, flags); - - if (fdtv) { - packet = iso->first_packet; - num = hpsb_iso_n_ready(iso); - for (i = 0; i < num; i++, - packet = (packet + 1) % iso->buf_packets) { - unsigned char *buf = - dma_region_i(&iso->data_buf, unsigned char, - iso->infos[packet].offset + - sizeof(struct CIPHeader)); - int count = (iso->infos[packet].len - - sizeof(struct CIPHeader)) / - (188 + sizeof(struct firewireheader)); - if (iso->infos[packet].len <= sizeof(struct CIPHeader)) - continue; // ignore empty packet - - while (count --) { - if (buf[sizeof(struct firewireheader)] == 0x47) - dvb_dmx_swfilter_packets(&fdtv->demux, - &buf[sizeof(struct firewireheader)], 1); - else - printk("%s: invalid packet, skipping\n", __func__); - buf += 188 + sizeof(struct firewireheader); - - } - - } - hpsb_iso_recv_release_packets(iso, num); - } - else { - printk("%s: packets for unknown iso channel, skipping\n", - __func__); - hpsb_iso_recv_release_packets(iso, hpsb_iso_n_ready(iso)); - } -} - diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c index 436c0c69a13d..46a6324d7b73 100644 --- a/drivers/media/dvb/firewire/firedtv-rc.c +++ b/drivers/media/dvb/firewire/firedtv-rc.c @@ -15,7 +15,6 @@ #include <linux/string.h> #include <linux/types.h> -#include "firedtv-rc.h" #include "firedtv.h" /* fixed table with older keycodes, geared towards MythTV */ diff --git a/drivers/media/dvb/firewire/firedtv-rc.h b/drivers/media/dvb/firewire/firedtv-rc.h deleted file mode 100644 index d3e14727d3dd..000000000000 --- a/drivers/media/dvb/firewire/firedtv-rc.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _FIREDTV_RC_H -#define _FIREDTV_RC_H - -struct firedtv; -struct device; - -int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); -void fdtv_unregister_rc(struct firedtv *fdtv); -void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code); - -#endif /* _FIREDTV_RC_H */ diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h index 2a34028ccbcd..d48530b81e61 100644 --- a/drivers/media/dvb/firewire/firedtv.h +++ b/drivers/media/dvb/firewire/firedtv.h @@ -29,95 +29,35 @@ #include <dvb_net.h> #include <dvbdev.h> -#include <linux/version.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) -#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w, v) -#else -#define DVB_REGISTER_ADAPTER(x, y, z, w, v) dvb_register_adapter(x, y, z, w) -#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(x) -#endif - -/***************************************************************** - * CA message command constants from en50221_app_tags.h of libdvb - *****************************************************************/ -/* Resource Manager */ -#define TAG_PROFILE_ENQUIRY 0x9f8010 -#define TAG_PROFILE 0x9f8011 -#define TAG_PROFILE_CHANGE 0x9f8012 - -/* Application Info */ -#define TAG_APP_INFO_ENQUIRY 0x9f8020 -#define TAG_APP_INFO 0x9f8021 -#define TAG_ENTER_MENU 0x9f8022 - -/* CA Support */ -#define TAG_CA_INFO_ENQUIRY 0x9f8030 -#define TAG_CA_INFO 0x9f8031 -#define TAG_CA_PMT 0x9f8032 -#define TAG_CA_PMT_REPLY 0x9f8033 - -/* Host Control */ -#define TAG_TUNE 0x9f8400 -#define TAG_REPLACE 0x9f8401 -#define TAG_CLEAR_REPLACE 0x9f8402 -#define TAG_ASK_RELEASE 0x9f8403 - -/* Date and Time */ -#define TAG_DATE_TIME_ENQUIRY 0x9f8440 -#define TAG_DATE_TIME 0x9f8441 - -/* Man Machine Interface (MMI) */ -#define TAG_CLOSE_MMI 0x9f8800 -#define TAG_DISPLAY_CONTROL 0x9f8801 -#define TAG_DISPLAY_REPLY 0x9f8802 -#define TAG_TEXT_LAST 0x9f8803 -#define TAG_TEXT_MORE 0x9f8804 -#define TAG_KEYPAD_CONTROL 0x9f8805 -#define TAG_KEYPRESS 0x9f8806 -#define TAG_ENQUIRY 0x9f8807 -#define TAG_ANSWER 0x9f8808 -#define TAG_MENU_LAST 0x9f8809 -#define TAG_MENU_MORE 0x9f880a -#define TAG_MENU_ANSWER 0x9f880b -#define TAG_LIST_LAST 0x9f880c -#define TAG_LIST_MORE 0x9f880d -#define TAG_SUBTITLE_SEGMENT_LAST 0x9f880e -#define TAG_SUBTITLE_SEGMENT_MORE 0x9f880f -#define TAG_DISPLAY_MESSAGE 0x9f8810 -#define TAG_SCENE_END_MARK 0x9f8811 -#define TAG_SCENE_DONE 0x9f8812 -#define TAG_SCENE_CONTROL 0x9f8813 -#define TAG_SUBTITLE_DOWNLOAD_LAST 0x9f8814 -#define TAG_SUBTITLE_DOWNLOAD_MORE 0x9f8815 -#define TAG_FLUSH_DOWNLOAD 0x9f8816 -#define TAG_DOWNLOAD_REPLY 0x9f8817 - -/* Low Speed Communications */ -#define TAG_COMMS_COMMAND 0x9f8c00 -#define TAG_CONNECTION_DESCRIPTOR 0x9f8c01 -#define TAG_COMMS_REPLY 0x9f8c02 -#define TAG_COMMS_SEND_LAST 0x9f8c03 -#define TAG_COMMS_SEND_MORE 0x9f8c04 -#define TAG_COMMS_RECV_LAST 0x9f8c05 -#define TAG_COMMS_RECV_MORE 0x9f8c06 - -/* Authentication */ -#define TAG_AUTH_REQ 0x9f8200 -#define TAG_AUTH_RESP 0x9f8201 - -/* Teletext */ -#define TAG_TELETEXT_EBU 0x9f9000 - -/* Smartcard */ -#define TAG_SMARTCARD_COMMAND 0x9f8e00 -#define TAG_SMARTCARD_REPLY 0x9f8e01 -#define TAG_SMARTCARD_SEND 0x9f8e02 -#define TAG_SMARTCARD_RCV 0x9f8e03 - -/* EPG */ -#define TAG_EPG_ENQUIRY 0x9f8f00 -#define TAG_EPG_REPLY 0x9f8f01 - +struct firedtv_tuner_status { + unsigned active_system:8; + unsigned searching:1; + unsigned moving:1; + unsigned no_rf:1; + unsigned input:1; + unsigned selected_antenna:7; + unsigned ber:32; + unsigned signal_strength:8; + unsigned raster_frequency:2; + unsigned rf_frequency:22; + unsigned man_dep_info_length:8; + unsigned front_end_error:1; + unsigned antenna_error:1; + unsigned front_end_power_status:1; + unsigned power_supply:1; + unsigned carrier_noise_ratio:16; + unsigned power_supply_voltage:8; + unsigned antenna_voltage:8; + unsigned firewire_bus_voltage:8; + unsigned ca_mmi:1; + unsigned ca_pmt_reply:1; + unsigned ca_date_time_request:1; + unsigned ca_application_info:1; + unsigned ca_module_present_status:1; + unsigned ca_dvb_flag:1; + unsigned ca_error_flag:1; + unsigned ca_initialization_status:1; +}; enum model_type { FIREDTV_UNKNOWN = 0, @@ -127,11 +67,22 @@ enum model_type { FIREDTV_DVB_S2 = 4, }; +struct device; struct input_dev; -struct hpsb_iso; -struct unit_directory; +struct firedtv; + +struct firedtv_backend { + int (*lock)(struct firedtv *fdtv, u64 addr, void *data, __be32 arg); + int (*read)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*start_iso)(struct firedtv *fdtv); + void (*stop_iso)(struct firedtv *fdtv); +}; struct firedtv { + struct device *device; + struct list_head list; + struct dvb_adapter adapter; struct dmxdev dmxdev; struct dvb_demux demux; @@ -149,79 +100,83 @@ struct firedtv { struct work_struct remote_ctrl_work; struct input_dev *remote_ctrl_dev; - struct firedtv_channel { - bool active; - int pid; - } channel[16]; - struct mutex demux_mutex; + enum model_type type; + char subunit; + char isochannel; + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t tone; - struct unit_directory *ud; + const struct firedtv_backend *backend; + void *backend_data; - enum model_type type; - char subunit; - fe_sec_voltage_t voltage; - fe_sec_tone_mode_t tone; - - int isochannel; - struct hpsb_iso *iso_handle; - - struct list_head list; + struct mutex demux_mutex; + unsigned long channel_active; + u16 channel_pid[16]; - /* needed by avc_api */ - int resp_length; - u8 respfrm[512]; + size_t response_length; + u8 response[512]; }; -struct firewireheader { - union { - struct { - __u8 tcode:4; - __u8 sy:4; - __u8 tag:2; - __u8 channel:6; - - __u8 length_l; - __u8 length_h; - } hdr; - __u32 val; - }; -}; - -struct CIPHeader { - union { - struct { - __u8 syncbits:2; - __u8 sid:6; - __u8 dbs; - __u8 fn:2; - __u8 qpc:3; - __u8 sph:1; - __u8 rsv:2; - __u8 dbc; - __u8 syncbits2:2; - __u8 fmt:6; - __u32 fdf:24; - } cip; - __u64 val; - }; -}; - -extern const char *fdtv_model_names[]; -extern struct list_head fdtv_list; -extern spinlock_t fdtv_list_lock; +/* firedtv-1394.c */ +#ifdef CONFIG_DVB_FIREDTV_IEEE1394 +int fdtv_1394_init(struct ieee1394_device_id id_table[]); +void fdtv_1394_exit(void); +#else +static inline int fdtv_1394_init(struct ieee1394_device_id it[]) { return 0; } +static inline void fdtv_1394_exit(void) {} +#endif -struct device; +/* firedtv-avc.c */ +int avc_recv(struct firedtv *fdtv, void *data, size_t length); +int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat); +struct dvb_frontend_parameters; +int avc_tuner_dsd(struct firedtv *fdtv, struct dvb_frontend_parameters *params); +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]); +int avc_tuner_get_ts(struct firedtv *fdtv); +int avc_identify_subunit(struct firedtv *fdtv); +struct dvb_diseqc_master_cmd; +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd); +void avc_remote_ctrl_work(struct work_struct *work); +int avc_register_remote_control(struct firedtv *fdtv); +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_reset(struct firedtv *fdtv); +int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length); +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval); +int avc_ca_enter_menu(struct firedtv *fdtv); +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len); +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel); +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel); + +/* firedtv-ci.c */ +int fdtv_ca_register(struct firedtv *fdtv); +void fdtv_ca_release(struct firedtv *fdtv); /* firedtv-dvb.c */ int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed); int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed); -int fdtv_dvbdev_init(struct firedtv *fdtv, struct device *dev); +int fdtv_dvb_register(struct firedtv *fdtv); +void fdtv_dvb_unregister(struct firedtv *fdtv); +struct firedtv *fdtv_alloc(struct device *dev, + const struct firedtv_backend *backend, + const char *name, size_t name_len); +extern const char *fdtv_model_names[]; /* firedtv-fe.c */ void fdtv_frontend_init(struct firedtv *fdtv); -/* firedtv-iso.c */ -int setup_iso_channel(struct firedtv *fdtv); -void tear_down_iso_channel(struct firedtv *fdtv); +/* firedtv-rc.c */ +#ifdef CONFIG_DVB_FIREDTV_INPUT +int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); +void fdtv_unregister_rc(struct firedtv *fdtv); +void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code); +#else +static inline int fdtv_register_rc(struct firedtv *fdtv, + struct device *dev) { return 0; } +static inline void fdtv_unregister_rc(struct firedtv *fdtv) {} +static inline void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) {} +#endif #endif /* _FIREDTV_H */ -- cgit v1.2.3 From e73bf9f135fe1e5db646e668676d22af3008e0c0 Mon Sep 17 00:00:00 2001 From: Beat Michel Liechti <bml303@gmail.com> Date: Tue, 24 Feb 2009 15:52:49 +0100 Subject: firedtv: dvb_frontend_info for FireDTV S2, fix "frequency limits undefined" error I found that the function fdtv_frontend_init in the file firedtv-fe.c was missing a case for FIREDTV_DVB_S2 which resulted in "frequency limits undefined" errors in syslog. Signed-off-by: Beat Michel Liechti <bml303@gmail.com> Change by Stefan R: combine it with case case FIREDTV_DVB_S as originally suggested by Beat Michel. This enables FE_CAN_FEC_AUTO also for FireDTV-S2 devices which is possible as long as only DVB-S channels are used. FE_CAN_FEC_AUTO would be wrong for DVB-S2 channels, but those cannot be used yet since the driver is not yet converted to S2API. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/media/dvb/firewire/firedtv-fe.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c index 9b9539c800f8..7ba43630a25d 100644 --- a/drivers/media/dvb/firewire/firedtv-fe.c +++ b/drivers/media/dvb/firewire/firedtv-fe.c @@ -185,6 +185,7 @@ void fdtv_frontend_init(struct firedtv *fdtv) switch (fdtv->type) { case FIREDTV_DVB_S: + case FIREDTV_DVB_S2: fi->type = FE_QPSK; fi->frequency_min = 950000; -- cgit v1.2.3 From f29d2e0275a4f03ef2fd158e484508dcb0c64efb Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Tue, 24 Feb 2009 19:19:48 +0100 Subject: i2c: Fix misplaced parentheses Fix misplaced parentheses. Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Signed-off-by: Jean Delvare <khali@linux-fr.org> --- drivers/i2c/i2c-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index b1c9abe24c7b..e7d984866de0 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1831,7 +1831,8 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, case I2C_SMBUS_QUICK: msg[0].len = 0; /* Special case: The read/write field is used as data */ - msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0; + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); num = 1; break; case I2C_SMBUS_BYTE: -- cgit v1.2.3 From a746b578d8406b2db0e9f0d040061bc1f78433cf Mon Sep 17 00:00:00 2001 From: Roel Kluin <roel.kluin@gmail.com> Date: Tue, 24 Feb 2009 19:19:48 +0100 Subject: i2c: Timeouts reach -1 With a postfix decrement these timeouts reach -1 rather than 0, but after the loop it is tested whether they have become 0. As pointed out by Jean Delvare, the condition we are waiting for should also be tested before the timeout. With the current order, you could exit with a timeout error while the job is actually done. Signed-off-by: Roel Kluin <roel.kluin@gmail.com> Signed-off-by: Jean Delvare <khali@linux-fr.org> --- drivers/i2c/busses/i2c-amd8111.c | 4 ++-- drivers/i2c/busses/i2c-pxa.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index edab51973bf5..a7c59908c457 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -72,7 +72,7 @@ static unsigned int amd_ec_wait_write(struct amd_smbus *smbus) { int timeout = 500; - while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF)) + while ((inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF) && --timeout) udelay(1); if (!timeout) { @@ -88,7 +88,7 @@ static unsigned int amd_ec_wait_read(struct amd_smbus *smbus) { int timeout = 500; - while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF)) + while ((~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF) && --timeout) udelay(1); if (!timeout) { diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 6af68146c342..bdb1f7510e91 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -644,7 +644,7 @@ static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, i2c_pxa_start_message(i2c); - while (timeout-- && i2c->msg_num > 0) { + while (i2c->msg_num > 0 && --timeout) { i2c_pxa_handler(0, i2c); udelay(10); } -- cgit v1.2.3 From cd97f39b7cdf1c8a9c9f52865eec795b7f0c811d Mon Sep 17 00:00:00 2001 From: Jean Delvare <khali@linux-fr.org> Date: Tue, 24 Feb 2009 19:19:49 +0100 Subject: i2c-dev: Clarify the unit of ioctl I2C_TIMEOUT The unit in which user-space can set the bus timeout value is jiffies for historical reasons (back when HZ was always 100.) This is however not good because user-space doesn't know how long a jiffy lasts. The timeout value should instead be set in a fixed time unit. Given the original value of HZ, this unit should be 10 ms, for compatibility. Signed-off-by: Jean Delvare <khali@linux-fr.org> Acked-by: Wolfram Sang <w.sang@pengutronix.de> --- drivers/i2c/i2c-dev.c | 6 +++++- include/linux/i2c-dev.h | 2 +- include/linux/i2c.h | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index c171988a9f51..7e13d2df9af3 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -35,6 +35,7 @@ #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <linux/smp_lock.h> +#include <linux/jiffies.h> #include <asm/uaccess.h> static struct i2c_driver i2cdev_driver; @@ -422,7 +423,10 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) client->adapter->retries = arg; break; case I2C_TIMEOUT: - client->adapter->timeout = arg; + /* For historical reasons, user-space sets the timeout + * value in units of 10 ms. + */ + client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: /* NOTE: returning a fault code here could cause trouble diff --git a/include/linux/i2c-dev.h b/include/linux/i2c-dev.h index 311315b56b61..fd53bfd26470 100644 --- a/include/linux/i2c-dev.h +++ b/include/linux/i2c-dev.h @@ -33,7 +33,7 @@ */ #define I2C_RETRIES 0x0701 /* number of times a device address should be polled when not acknowledging */ -#define I2C_TIMEOUT 0x0702 /* set timeout in jiffies - call with int */ +#define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms */ /* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses * are NOT supported! (due to code brokenness) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index fcfbfea3af72..c86c3b07604c 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -361,7 +361,7 @@ struct i2c_adapter { struct mutex bus_lock; struct mutex clist_lock; - int timeout; + int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ -- cgit v1.2.3 From 082a4cf80966ebcd08bf775cd258171cdd85c1a1 Mon Sep 17 00:00:00 2001 From: Jean Delvare <khali@linux-fr.org> Date: Tue, 24 Feb 2009 19:19:49 +0100 Subject: i2c: Make sure i2c_algo_bit_data.timeout is HZ-independent i2c_algo_bit_data.timeout is supposed to be in jiffies, so drivers should use set this value in terms of HZ. Ultimately I think this field should be discarded in favor of i2c_adapter.timeout, but that's left for a future patch. Signed-off-by: Jean Delvare <khali@linux-fr.org> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> Acked-by: Lennert Buytenhek <kernel@wantstofly.org> Acked-by: Len Sorensen <lsorense@csclub.uwaterloo.ca> --- drivers/i2c/busses/i2c-acorn.c | 2 +- drivers/i2c/busses/i2c-ixp2000.c | 2 +- drivers/i2c/busses/scx200_i2c.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c index 9fee3ca17344..dddccdd91f93 100644 --- a/drivers/i2c/busses/i2c-acorn.c +++ b/drivers/i2c/busses/i2c-acorn.c @@ -79,7 +79,7 @@ static struct i2c_algo_bit_data ioc_data = { .getsda = ioc_getsda, .getscl = ioc_getscl, .udelay = 80, - .timeout = 100 + .timeout = HZ, }; static struct i2c_adapter ioc_ops = { diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c index 8e8467970481..c016f7a2c5fc 100644 --- a/drivers/i2c/busses/i2c-ixp2000.c +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -114,7 +114,7 @@ static int ixp2000_i2c_probe(struct platform_device *plat_dev) drv_data->algo_data.getsda = ixp2000_bit_getsda; drv_data->algo_data.getscl = ixp2000_bit_getscl; drv_data->algo_data.udelay = 6; - drv_data->algo_data.timeout = 100; + drv_data->algo_data.timeout = HZ; strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, sizeof(drv_data->adapter.name)); diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c index 162b74a04886..42df0eca43d5 100644 --- a/drivers/i2c/busses/scx200_i2c.c +++ b/drivers/i2c/busses/scx200_i2c.c @@ -76,7 +76,7 @@ static struct i2c_algo_bit_data scx200_i2c_data = { .getsda = scx200_i2c_getsda, .getscl = scx200_i2c_getscl, .udelay = 10, - .timeout = 100, + .timeout = HZ, }; static struct i2c_adapter scx200_i2c_ops = { -- cgit v1.2.3 From 531660ef5604c75de6fdead9da1304051af17c09 Mon Sep 17 00:00:00 2001 From: Russell King <rmk+kernel@arm.linux.org.uk> Date: Tue, 24 Feb 2009 19:19:50 +0100 Subject: Add i2c_board_info for RiscPC PCF8583 Add the necessary i2c_board_info structure to fix the lack of PCF8583 RTC on RiscPC. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Jean Delvare <khali@linux-fr.org> Cc: Alessandro Zummo <a.zummo@towertech.it> --- arch/arm/mach-rpc/riscpc.c | 6 ++++++ drivers/i2c/busses/i2c-acorn.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/arch/arm/mach-rpc/riscpc.c b/arch/arm/mach-rpc/riscpc.c index e88d417736af..c7fc01e9d1f6 100644 --- a/arch/arm/mach-rpc/riscpc.c +++ b/arch/arm/mach-rpc/riscpc.c @@ -19,6 +19,7 @@ #include <linux/serial_8250.h> #include <linux/ata_platform.h> #include <linux/io.h> +#include <linux/i2c.h> #include <asm/elf.h> #include <asm/mach-types.h> @@ -201,8 +202,13 @@ static struct platform_device *devs[] __initdata = { &pata_device, }; +static struct i2c_board_info i2c_rtc = { + I2C_BOARD_INFO("pcf8583", 0x50) +}; + static int __init rpc_init(void) { + i2c_register_board_info(0, &i2c_rtc, 1); return platform_add_devices(devs, ARRAY_SIZE(devs)); } diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c index dddccdd91f93..9aefb5e5864d 100644 --- a/drivers/i2c/busses/i2c-acorn.c +++ b/drivers/i2c/busses/i2c-acorn.c @@ -83,6 +83,7 @@ static struct i2c_algo_bit_data ioc_data = { }; static struct i2c_adapter ioc_ops = { + .nr = 0, .algo_data = &ioc_data, }; @@ -90,7 +91,7 @@ static int __init i2c_ioc_init(void) { force_ones = FORCE_ONES | SCL | SDA; - return i2c_bit_add_bus(&ioc_ops); + return i2c_bit_add_numbered_bus(&ioc_ops); } module_init(i2c_ioc_init); -- cgit v1.2.3 From 3255aa2eb636a508fc82a73fabbb8aaf2ff23c0f Mon Sep 17 00:00:00 2001 From: Ingo Molnar <mingo@elte.hu> Date: Wed, 25 Feb 2009 08:21:52 +0100 Subject: x86, mm: pass in 'total' to __copy_from_user_*nocache() Impact: cleanup, enable future change Add a 'total bytes copied' parameter to __copy_from_user_*nocache(), and update all the callsites. The parameter is not used yet - architecture code can use it to more intelligently decide whether the copy should be cached or non-temporal. Cc: Salman Qazi <sqazi@google.com> Cc: Nick Piggin <npiggin@suse.de> Cc: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- arch/x86/include/asm/uaccess_32.h | 4 ++-- arch/x86/include/asm/uaccess_64.h | 5 ++--- drivers/gpu/drm/i915/i915_gem.c | 2 +- include/linux/uaccess.h | 4 ++-- mm/filemap.c | 10 ++++++---- mm/filemap_xip.c | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h index 5e06259e90e5..a0ba61386972 100644 --- a/arch/x86/include/asm/uaccess_32.h +++ b/arch/x86/include/asm/uaccess_32.h @@ -157,7 +157,7 @@ __copy_from_user(void *to, const void __user *from, unsigned long n) } static __always_inline unsigned long __copy_from_user_nocache(void *to, - const void __user *from, unsigned long n) + const void __user *from, unsigned long n, unsigned long total) { might_fault(); if (__builtin_constant_p(n)) { @@ -180,7 +180,7 @@ static __always_inline unsigned long __copy_from_user_nocache(void *to, static __always_inline unsigned long __copy_from_user_inatomic_nocache(void *to, const void __user *from, - unsigned long n) + unsigned long n, unsigned long total) { return __copy_from_user_ll_nocache_nozero(to, from, n); } diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index 987a2c10fe20..a748253db0c9 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -189,7 +189,7 @@ extern long __copy_user_nocache(void *dst, const void __user *src, unsigned size, int zerorest); static inline int __copy_from_user_nocache(void *dst, const void __user *src, - unsigned size) + unsigned size, unsigned long total) { might_sleep(); /* @@ -205,8 +205,7 @@ static inline int __copy_from_user_nocache(void *dst, const void __user *src, } static inline int __copy_from_user_inatomic_nocache(void *dst, - const void __user *src, - unsigned size) + const void __user *src, unsigned size, unsigned total) { if (likely(size >= PAGE_SIZE)) return __copy_user_nocache(dst, src, size, 0); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 818576654092..6b209db8370d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -215,7 +215,7 @@ fast_user_write(struct io_mapping *mapping, vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base); unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset, - user_data, length); + user_data, length, length); io_mapping_unmap_atomic(vaddr_atomic); if (unwritten) return -EFAULT; diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 6b58367d145e..6f3c603b0d67 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -41,13 +41,13 @@ static inline void pagefault_enable(void) #ifndef ARCH_HAS_NOCACHE_UACCESS static inline unsigned long __copy_from_user_inatomic_nocache(void *to, - const void __user *from, unsigned long n) + const void __user *from, unsigned long n, unsigned long total) { return __copy_from_user_inatomic(to, from, n); } static inline unsigned long __copy_from_user_nocache(void *to, - const void __user *from, unsigned long n) + const void __user *from, unsigned long n, unsigned long total) { return __copy_from_user(to, from, n); } diff --git a/mm/filemap.c b/mm/filemap.c index 23acefe51808..60fd56772cc6 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1816,14 +1816,14 @@ EXPORT_SYMBOL(file_remove_suid); static size_t __iovec_copy_from_user_inatomic(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) { - size_t copied = 0, left = 0; + size_t copied = 0, left = 0, total = bytes; while (bytes) { char __user *buf = iov->iov_base + base; int copy = min(bytes, iov->iov_len - base); base = 0; - left = __copy_from_user_inatomic_nocache(vaddr, buf, copy); + left = __copy_from_user_inatomic_nocache(vaddr, buf, copy, total); copied += copy; bytes -= copy; vaddr += copy; @@ -1851,8 +1851,9 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, if (likely(i->nr_segs == 1)) { int left; char __user *buf = i->iov->iov_base + i->iov_offset; + left = __copy_from_user_inatomic_nocache(kaddr + offset, - buf, bytes); + buf, bytes, bytes); copied = bytes - left; } else { copied = __iovec_copy_from_user_inatomic(kaddr + offset, @@ -1880,7 +1881,8 @@ size_t iov_iter_copy_from_user(struct page *page, if (likely(i->nr_segs == 1)) { int left; char __user *buf = i->iov->iov_base + i->iov_offset; - left = __copy_from_user_nocache(kaddr + offset, buf, bytes); + + left = __copy_from_user_nocache(kaddr + offset, buf, bytes, bytes); copied = bytes - left; } else { copied = __iovec_copy_from_user_inatomic(kaddr + offset, diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 0c04615651b7..bf54f8a2cf1d 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -354,7 +354,7 @@ __xip_file_write(struct file *filp, const char __user *buf, break; copied = bytes - - __copy_from_user_nocache(xip_mem + offset, buf, bytes); + __copy_from_user_nocache(xip_mem + offset, buf, bytes, bytes); if (likely(copied > 0)) { status = copied; -- cgit v1.2.3 From 6644107d57a8fa82b47e4c55da4d9d91a612f29c Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> Date: Tue, 24 Feb 2009 17:35:11 -0800 Subject: gpu/drm, x86, PAT: Handle io_mapping_create_wc() errors in a clean way io_mapping_create_wc can return NULL on error and io_mapping_free() should be called on one of the error-cleanup path. Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com> Cc: Dave Airlie <airlied@redhat.com> Cc: Jesse Barnes <jbarnes@virtuousgeek.org> Cc: Eric Anholt <eric@anholt.net> Cc: Keith Packard <keithp@keithp.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- drivers/gpu/drm/i915/i915_dma.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2d797ffe8137..6949c2d58f1d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1090,6 +1090,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev_priv->mm.gtt_mapping = io_mapping_create_wc(dev->agp->base, dev->agp->agp_info.aper_size * 1024*1024); + if (dev_priv->mm.gtt_mapping == NULL) { + ret = -EIO; + goto out_rmmap; + } + /* Set up a WC MTRR for non-PAT systems. This is more common than * one would think, because the kernel disables PAT on first * generation Core chips because WC PAT gets overridden by a UC @@ -1122,7 +1127,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (!I915_NEED_GFX_HWS(dev)) { ret = i915_init_phys_hws(dev); if (ret != 0) - goto out_rmmap; + goto out_iomapfree; } /* On the 945G/GM, the chipset reports the MSI capability on the @@ -1161,6 +1166,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) return 0; +out_iomapfree: + io_mapping_free(dev_priv->mm.gtt_mapping); out_rmmap: iounmap(dev_priv->regs); free_priv: -- cgit v1.2.3