initial import
authorglevand <[email protected]>
Mon, 3 Feb 2014 11:07:39 +0000 (3 12:07 +0100)
committerglevand <[email protected]>
Mon, 3 Feb 2014 11:07:39 +0000 (3 12:07 +0100)
27 files changed:
0010-ps3stor-multiple-regions.patch [new file with mode: 0644]
0020-ps3fb-use-fifo.patch [new file with mode: 0644]
0030-ps3flash.patch [new file with mode: 0644]
0035-ps3-partition.patch [new file with mode: 0644]
0040-ps3sysmgr-lpar-reboot.patch [new file with mode: 0644]
0050-ps3sysmgr-char-device.patch [new file with mode: 0644]
0060-ps3avmgr-char-device.patch [new file with mode: 0644]
0070-ps3dispmgr.patch [new file with mode: 0644]
0080-ps3rom-vendor-specific-command.patch [new file with mode: 0644]
0090-spu-enum-shared-param.patch [new file with mode: 0644]
0100-lv1call-repo-node-lparid-param.patch [new file with mode: 0644]
0110-lv1call-add-hvcalls-114-115.patch [new file with mode: 0644]
0120-lv1call-add-storage-region-hvcalls.patch [new file with mode: 0644]
0130-ps3physmem.patch [new file with mode: 0644]
0140-ps3strgmngr.patch [new file with mode: 0644]
0150-ps3jupiter.patch [new file with mode: 0644]
0160-gelic-disable-eurus-ctrl-iface.patch [new file with mode: 0644]
0170-gelic-wireless-print-cmd-status.patch [new file with mode: 0644]
0180-lv1call-add-undocumented-spe-hvcalls.patch [new file with mode: 0644]
0190-export-spe-irq-setup-destroy.patch [new file with mode: 0644]
0200-export-event-receive-port-destroy.patch [new file with mode: 0644]
0210-ps3encdec.patch [new file with mode: 0644]
0220-spuisofs.patch [new file with mode: 0644]
0230-spuldrfs.patch [new file with mode: 0644]
0240-ps3lv1call.patch [new file with mode: 0644]
0250-lv1call-add-debug-console-hvcalls.patch [new file with mode: 0644]
0260-udbg-lv1-console.patch [new file with mode: 0644]

diff --git a/0010-ps3stor-multiple-regions.patch b/0010-ps3stor-multiple-regions.patch
new file mode 100644 (file)
index 0000000..10d29f4
--- /dev/null
@@ -0,0 +1,481 @@
+--- a/arch/powerpc/include/asm/ps3stor.h       2012-01-03 19:41:27.000000000 +0100
++++ b/arch/powerpc/include/asm/ps3stor.h       2012-01-05 16:38:31.006804326 +0100
+@@ -30,6 +30,7 @@
+       unsigned int id;
+       u64 start;
+       u64 size;
++      u64 flags;
+ };
+ struct ps3_storage_device {
+@@ -50,7 +51,6 @@
+       unsigned int num_regions;
+       unsigned long accessible_regions;
+-      unsigned int region_idx;                /* first accessible region */
+       struct ps3_storage_region regions[0];   /* Must be last */
+ };
+@@ -62,8 +62,8 @@
+ extern int ps3stor_setup(struct ps3_storage_device *dev,
+                        irq_handler_t handler);
+ extern void ps3stor_teardown(struct ps3_storage_device *dev);
+-extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+-                                    u64 start_sector, u64 sectors,
++extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, unsigned int region_idx,
++                                    u64 start_sector, u64 sectors, u64 flags,
+                                     int write);
+ extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd,
+                               u64 arg1, u64 arg2, u64 arg3, u64 arg4);
+--- a/drivers/ps3/ps3stor_lib.c        2012-01-05 16:42:51.670823329 +0100
++++ b/drivers/ps3/ps3stor_lib.c        2012-01-05 16:44:22.208906179 +0100
+@@ -101,9 +101,8 @@
+                       "%s:%u: checking accessibility of region %u\n",
+                       __func__, __LINE__, i);
+-              dev->region_idx = i;
+-              res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1,
+-                                               0);
++              res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, i, 0, 1,
++                                               dev->regions[i].flags, 0);
+               if (res) {
+                       dev_dbg(&dev->sbd.core, "%s:%u: read failed, "
+                               "region %u is not accessible\n", __func__,
+@@ -115,6 +114,10 @@
+                       __func__, __LINE__, i);
+               set_bit(i, &dev->accessible_regions);
++              dev_info(&dev->sbd.core,
++                       "accessible region %u start %llu size %llu\n",
++                       i, dev->regions[i].start, dev->regions[i].size);
++
+               /* We can access at least one region */
+               error = 0;
+       }
+@@ -124,14 +127,8 @@
+       n = hweight_long(dev->accessible_regions);
+       if (n > 1)
+               dev_info(&dev->sbd.core,
+-                       "%s:%u: %lu accessible regions found. Only the first "
+-                       "one will be used\n",
++                       "%s:%u: %lu accessible regions found\n",
+                        __func__, __LINE__, n);
+-      dev->region_idx = __ffs(dev->accessible_regions);
+-      dev_info(&dev->sbd.core,
+-               "First accessible region has index %u start %llu size %llu\n",
+-               dev->region_idx, dev->regions[dev->region_idx].start,
+-               dev->regions[dev->region_idx].size);
+       return 0;
+ }
+@@ -265,17 +262,19 @@
+  *    ps3stor_read_write_sectors - read/write from/to a storage device
+  *    @dev: Pointer to a struct ps3_storage_device
+  *    @lpar: HV logical partition address
++ *    @region_idx: Region index
+  *    @start_sector: First sector to read/write
+  *    @sectors: Number of sectors to read/write
++ *    @flags: Flags
+  *    @write: Flag indicating write (non-zero) or read (zero)
+  *
+  *    Returns 0 for success, -1 in case of failure to submit the command, or
+  *    an LV1 status value in case of other errors
+  */
+-u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+-                             u64 start_sector, u64 sectors, int write)
++u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, unsigned int region_idx,
++                             u64 start_sector, u64 sectors, u64 flags, int write)
+ {
+-      unsigned int region_id = dev->regions[dev->region_idx].id;
++      unsigned int region_id = dev->regions[region_idx].id;
+       const char *op = write ? "write" : "read";
+       int res;
+@@ -284,10 +283,10 @@
+       init_completion(&dev->done);
+       res = write ? lv1_storage_write(dev->sbd.dev_id, region_id,
+-                                      start_sector, sectors, 0, lpar,
++                                      start_sector, sectors, flags, lpar,
+                                       &dev->tag)
+                   : lv1_storage_read(dev->sbd.dev_id, region_id,
+-                                     start_sector, sectors, 0, lpar,
++                                     start_sector, sectors, flags, lpar,
+                                      &dev->tag);
+       if (res) {
+               dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+--- a/drivers/block/ps3disk.c  2013-02-14 11:22:05.716017676 +0100
++++ b/drivers/block/ps3disk.c  2013-02-14 11:19:47.200033102 +0100
+@@ -32,7 +32,8 @@
+ #define BOUNCE_SIZE           (64*1024)
+-#define PS3DISK_MAX_DISKS     16
++#define PS3DISK_MAX_NUM_REGS  8
++
+ #define PS3DISK_MINORS                16
+@@ -41,12 +42,13 @@
+ struct ps3disk_private {
+       spinlock_t lock;                /* Request queue spinlock */
+-      struct request_queue *queue;
+-      struct gendisk *gendisk;
+       unsigned int blocking_factor;
+       struct request *req;
+       u64 raw_capacity;
+       unsigned char model[ATA_ID_PROD_LEN+1];
++      struct gendisk *gendisk[PS3DISK_MAX_NUM_REGS];
++      struct request_queue *queue[PS3DISK_MAX_NUM_REGS];
++      int next_queue;
+ };
+@@ -88,6 +90,13 @@
+       .owner          = THIS_MODULE,
+ };
++static unsigned int region_flags[] =
++{
++      0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
++};
++module_param_array(region_flags, uint, NULL, S_IRUGO);
++MODULE_PARM_DESC(region_flags, "Region flags");
++
+ static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
+                                  struct request *req, int gather)
+@@ -126,7 +135,9 @@
+       int write = rq_data_dir(req), res;
+       const char *op = write ? "write" : "read";
+       u64 start_sector, sectors;
+-      unsigned int region_id = dev->regions[dev->region_idx].id;
++      unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) / PS3DISK_MINORS;
++      unsigned int region_id = dev->regions[region_idx].id;
++      unsigned int region_flags = dev->regions[region_idx].flags;
+ #ifdef DEBUG
+       unsigned int n = 0;
+@@ -149,11 +160,11 @@
+               ps3disk_scatter_gather(dev, req, 1);
+               res = lv1_storage_write(dev->sbd.dev_id, region_id,
+-                                      start_sector, sectors, 0,
++                                      start_sector, sectors, region_flags,
+                                       dev->bounce_lpar, &dev->tag);
+       } else {
+               res = lv1_storage_read(dev->sbd.dev_id, region_id,
+-                                     start_sector, sectors, 0,
++                                     start_sector, sectors, region_flags,
+                                      dev->bounce_lpar, &dev->tag);
+       }
+       if (res) {
+@@ -232,6 +243,8 @@
+       int res, read, error;
+       u64 tag, status;
+       const char *op;
++      struct request_queue *q;
++      int old_queue;
+       res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+@@ -279,7 +292,20 @@
+       spin_lock(&priv->lock);
+       __blk_end_request_all(req, error);
+       priv->req = NULL;
+-      ps3disk_do_request(dev, priv->queue);
++      old_queue = priv->next_queue;
++      do {
++              q = priv->queue[priv->next_queue];
++
++              priv->next_queue++;
++              if (priv->next_queue >= dev->num_regions)
++                      priv->next_queue = 0;
++
++              if (q) {
++                      ps3disk_do_request(dev, q);
++                      if (priv->req)
++                              break;
++              }
++      } while (old_queue != priv->next_queue);
+       spin_unlock(&priv->lock);
+       return IRQ_HANDLED;
+@@ -397,19 +423,17 @@
+       return 0;
+ }
+-static unsigned long ps3disk_mask;
+-
+-static DEFINE_MUTEX(ps3disk_mask_mutex);
+-
+ static int ps3disk_probe(struct ps3_system_bus_device *_dev)
+ {
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       struct ps3disk_private *priv;
+       int error;
+-      unsigned int devidx;
++      unsigned int regidx, devidx;
+       struct request_queue *queue;
+       struct gendisk *gendisk;
++      BUG_ON(dev->num_regions > PS3DISK_MAX_NUM_REGS);
++
+       if (dev->blk_size < 512) {
+               dev_err(&dev->sbd.core,
+                       "%s:%u: cannot handle block size %llu\n", __func__,
+@@ -417,18 +441,6 @@
+               return -EINVAL;
+       }
+-      BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG);
+-      mutex_lock(&ps3disk_mask_mutex);
+-      devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS);
+-      if (devidx >= PS3DISK_MAX_DISKS) {
+-              dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
+-                      __LINE__);
+-              mutex_unlock(&ps3disk_mask_mutex);
+-              return -ENOSPC;
+-      }
+-      __set_bit(devidx, &ps3disk_mask);
+-      mutex_unlock(&ps3disk_mask_mutex);
+-
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               error = -ENOMEM;
+@@ -445,67 +457,85 @@
+               goto fail_free_priv;
+       }
++      for (regidx = 0; regidx < dev->num_regions; regidx++)
++              dev->regions[regidx].flags = region_flags[regidx];
++
+       error = ps3stor_setup(dev, ps3disk_interrupt);
+       if (error)
+               goto fail_free_bounce;
+       ps3disk_identify(dev);
+-      queue = blk_init_queue(ps3disk_request, &priv->lock);
+-      if (!queue) {
+-              dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
+-                      __func__, __LINE__);
+-              error = -ENOMEM;
+-              goto fail_teardown;
+-      }
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (test_bit(devidx, &dev->accessible_regions) == 0)
++                      continue;
+-      priv->queue = queue;
+-      queue->queuedata = dev;
++              queue = blk_init_queue(ps3disk_request, &priv->lock);
++              if (!queue) {
++                      dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
++                              __func__, __LINE__);
++                      error = -ENOMEM;
++                      goto fail_cleanup;
++              }
+-      blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
++              priv->queue[devidx] = queue;
++              queue->queuedata = dev;
+-      blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
+-      blk_queue_segment_boundary(queue, -1UL);
+-      blk_queue_dma_alignment(queue, dev->blk_size-1);
+-      blk_queue_logical_block_size(queue, dev->blk_size);
++              blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
+-      blk_queue_flush(queue, REQ_FLUSH);
++              blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
++              blk_queue_segment_boundary(queue, -1UL);
++              blk_queue_dma_alignment(queue, dev->blk_size-1);
++              blk_queue_logical_block_size(queue, dev->blk_size);
++
++              blk_queue_flush(queue, REQ_FLUSH);
++
++              blk_queue_max_segments(queue, -1);
++              blk_queue_max_segment_size(queue, dev->bounce_size);
++
++              gendisk = alloc_disk(PS3DISK_MINORS);
++              if (!gendisk) {
++                      dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
++                              __LINE__);
++                      error = -ENOMEM;
++                      goto fail_cleanup;
++              }
+-      blk_queue_max_segments(queue, -1);
+-      blk_queue_max_segment_size(queue, dev->bounce_size);
++              priv->gendisk[devidx] = gendisk;
++              gendisk->major = ps3disk_major;
++              gendisk->first_minor = devidx * PS3DISK_MINORS;
++              gendisk->fops = &ps3disk_fops;
++              gendisk->queue = queue;
++              gendisk->private_data = dev;
++              gendisk->driverfs_dev = &dev->sbd.core;
++              snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME,
++                       devidx+'a');
++              priv->blocking_factor = dev->blk_size >> 9;
++              set_capacity(gendisk,
++                       dev->regions[devidx].size*priv->blocking_factor);
++
++              dev_info(&dev->sbd.core,
++                       "%s is a %s (%llu MiB total, %lu MiB region)\n",
++                       gendisk->disk_name, priv->model, priv->raw_capacity >> 11,
++                       get_capacity(gendisk) >> 11);
+-      gendisk = alloc_disk(PS3DISK_MINORS);
+-      if (!gendisk) {
+-              dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
+-                      __LINE__);
+-              error = -ENOMEM;
+-              goto fail_cleanup_queue;
++              add_disk(gendisk);
+       }
+-      priv->gendisk = gendisk;
+-      gendisk->major = ps3disk_major;
+-      gendisk->first_minor = devidx * PS3DISK_MINORS;
+-      gendisk->fops = &ps3disk_fops;
+-      gendisk->queue = queue;
+-      gendisk->private_data = dev;
+-      gendisk->driverfs_dev = &dev->sbd.core;
+-      snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME,
+-               devidx+'a');
+-      priv->blocking_factor = dev->blk_size >> 9;
+-      set_capacity(gendisk,
+-                   dev->regions[dev->region_idx].size*priv->blocking_factor);
+-
+-      dev_info(&dev->sbd.core,
+-               "%s is a %s (%llu MiB total, %lu MiB for OtherOS)\n",
+-               gendisk->disk_name, priv->model, priv->raw_capacity >> 11,
+-               get_capacity(gendisk) >> 11);
+-
+-      add_disk(gendisk);
+       return 0;
+-fail_cleanup_queue:
+-      blk_cleanup_queue(queue);
+-fail_teardown:
++fail_cleanup:
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (priv->gendisk[devidx]) {
++                      del_gendisk(priv->gendisk[devidx]);
++                      put_disk(priv->gendisk[devidx]);
++              }
++
++              if (priv->queue[devidx])
++                      blk_cleanup_queue(priv->queue[devidx]);
++      }
+       ps3stor_teardown(dev);
+ fail_free_bounce:
+       kfree(dev->bounce_buf);
+@@ -513,9 +543,6 @@
+       kfree(priv);
+       ps3_system_bus_set_drvdata(_dev, NULL);
+ fail:
+-      mutex_lock(&ps3disk_mask_mutex);
+-      __clear_bit(devidx, &ps3disk_mask);
+-      mutex_unlock(&ps3disk_mask_mutex);
+       return error;
+ }
+@@ -523,14 +550,19 @@
+ {
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      unsigned int devidx;
++
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (priv->gendisk[devidx]) {
++                      del_gendisk(priv->gendisk[devidx]);
++                      put_disk(priv->gendisk[devidx]);
++              }
++
++              if (priv->queue[devidx])
++                      blk_cleanup_queue(priv->queue[devidx]);
++      }
+-      mutex_lock(&ps3disk_mask_mutex);
+-      __clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS,
+-                  &ps3disk_mask);
+-      mutex_unlock(&ps3disk_mask_mutex);
+-      del_gendisk(priv->gendisk);
+-      blk_cleanup_queue(priv->queue);
+-      put_disk(priv->gendisk);
+       dev_notice(&dev->sbd.core, "Synchronizing disk cache\n");
+       ps3disk_sync_cache(dev);
+       ps3stor_teardown(dev);
+--- a/drivers/scsi/ps3rom.c    2012-01-03 19:41:27.000000000 +0100
++++ b/drivers/scsi/ps3rom.c    2012-01-05 16:38:58.103891705 +0100
+@@ -72,6 +72,14 @@
+ };
++static unsigned int region_flags[] =
++{
++      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
++};
++module_param_array(region_flags, uint, NULL, S_IRUGO);
++MODULE_PARM_DESC(region_flags, "Region flags");
++
++
+ static int ps3rom_slave_configure(struct scsi_device *scsi_dev)
+ {
+       struct ps3rom_private *priv = shost_priv(scsi_dev->host);
+@@ -172,12 +180,13 @@
+                              u32 sectors)
+ {
+       int res;
++      unsigned int region_idx = 0;
+       dev_dbg(&dev->sbd.core, "%s:%u: read %u sectors starting at %u\n",
+               __func__, __LINE__, sectors, start_sector);
+       res = lv1_storage_read(dev->sbd.dev_id,
+-                             dev->regions[dev->region_idx].id, start_sector,
++                             dev->regions[region_idx].id, start_sector,
+                              sectors, 0, dev->bounce_lpar, &dev->tag);
+       if (res) {
+               dev_err(&dev->sbd.core, "%s:%u: read failed %d\n", __func__,
+@@ -193,6 +202,7 @@
+                               u32 sectors)
+ {
+       int res;
++      unsigned int region_idx = 0;
+       dev_dbg(&dev->sbd.core, "%s:%u: write %u sectors starting at %u\n",
+               __func__, __LINE__, sectors, start_sector);
+@@ -200,7 +210,7 @@
+       scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size);
+       res = lv1_storage_write(dev->sbd.dev_id,
+-                              dev->regions[dev->region_idx].id, start_sector,
++                              dev->regions[region_idx].id, start_sector,
+                               sectors, 0, dev->bounce_lpar, &dev->tag);
+       if (res) {
+               dev_err(&dev->sbd.core, "%s:%u: write failed %d\n", __func__,
+@@ -362,6 +372,7 @@
+ {
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       int error;
++      unsigned int regidx;
+       struct Scsi_Host *host;
+       struct ps3rom_private *priv;
+@@ -377,6 +388,9 @@
+       if (!dev->bounce_buf)
+               return -ENOMEM;
++      for (regidx = 0; regidx < dev->num_regions; regidx++)
++              dev->regions[regidx].flags = region_flags[regidx];
++
+       error = ps3stor_setup(dev, ps3rom_interrupt);
+       if (error)
+               goto fail_free_bounce;
diff --git a/0020-ps3fb-use-fifo.patch b/0020-ps3fb-use-fifo.patch
new file mode 100644 (file)
index 0000000..16f7855
--- /dev/null
@@ -0,0 +1,537 @@
+--- a/arch/powerpc/include/asm/ps3gpu.h        2012-01-03 19:41:27.000000000 +0100
++++ b/arch/powerpc/include/asm/ps3gpu.h        2012-01-05 23:17:51.200679863 +0100
+@@ -25,6 +25,10 @@
+ #include <asm/lv1call.h>
++#define L1GPU_CONTEXT_ATTRIBUTE_FIFO_SETUP    0x1
++#define L1GPU_CONTEXT_ATTRIBUTE_FIFO_PAUSE    0x2
++#define L1GPU_CONTEXT_ATTRIBUTE_FIFO_RESUME   0x3
++
+ #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC  0x101
+ #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP  0x102
+@@ -44,11 +48,11 @@
+ static inline int lv1_gpu_display_sync(u64 context_handle, u64 head,
+-                                     u64 ddr_offset)
++                                     u64 sync_mode)
+ {
+       return lv1_gpu_context_attribute(context_handle,
+                                        L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
+-                                       head, ddr_offset, 0, 0);
++                                       head, sync_mode, 0, 0);
+ }
+ static inline int lv1_gpu_display_flip(u64 context_handle, u64 head,
+--- a/drivers/video/ps3fb.c    2012-01-03 19:41:27.000000000 +0100
++++ b/drivers/video/ps3fb.c    2012-01-05 23:17:32.550387632 +0100
+@@ -50,6 +50,7 @@
+ #define GPU_INTR_STATUS_VSYNC_0                       0       /* vsync on head A */
+ #define GPU_INTR_STATUS_VSYNC_1                       1       /* vsync on head B */
++#define GPU_INTR_STATUS_GRAPH_EXCEPTION               2       /* graphics exception */
+ #define GPU_INTR_STATUS_FLIP_0                        3       /* flip head A */
+ #define GPU_INTR_STATUS_FLIP_1                        4       /* flip head B */
+ #define GPU_INTR_STATUS_QUEUE_0                       5       /* queue head A */
+@@ -75,6 +76,22 @@
+       u32 reserved2;
+ };
++struct gpu_graph_exception_info {
++      u32 channel_id;
++      u32 cause;
++      u32 res1[8];
++      u32 dma_put;
++      u32 dma_get;
++      u32 call;
++      u32 jump;
++      u32 res2;
++      u32 fifo_put;
++      u32 fifo_get;
++      u32 fifo_ref;
++      u32 fifo_cache[512];
++      u32 graph_fifo[512];
++};
++
+ struct gpu_irq {
+       u32 irq_outlet;
+       u32 status;
+@@ -82,11 +99,8 @@
+       u32 video_cause;
+       u32 graph_cause;
+       u32 user_cause;
+-
+-      u32 res1;
+-      u64 res2;
+-
+-      u32 reserved[4];
++      u32 res[8];
++      struct gpu_graph_exception_info graph_exception_info;
+ };
+ struct gpu_driver_info {
+@@ -103,11 +117,27 @@
+       struct gpu_irq irq;
+ };
++struct gpu_fifo_ctrl {
++      u8 res[64];
++      u32 put;
++      u32 get;
++      u32 ref;
++};
++
++struct gpu_fifo {
++      volatile struct gpu_fifo_ctrl *ctrl;
++      u32 *start;
++      u32 *curr;
++      unsigned int len;
++      u32 ioif;
++};
++
+ struct ps3fb_priv {
+       unsigned int irq_no;
+       u64 context_handle, memory_handle;
+       struct gpu_driver_info *dinfo;
++      struct gpu_fifo fifo;
+       u64 vblank_count;       /* frame count */
+       wait_queue_head_t wait_vsync;
+@@ -260,8 +290,267 @@
+ static int ps3fb_mode;
+ module_param(ps3fb_mode, int, 0);
++static unsigned long ps3fb_gpu_ctx_flags = 0x820;
++module_param(ps3fb_gpu_ctx_flags, ulong, 0);
++
++static unsigned long ps3fb_gpu_mem_size[4];
++static int ps3fb_gpu_mem_size_count;
++module_param_array(ps3fb_gpu_mem_size, ulong, &ps3fb_gpu_mem_size_count, 0);
++
+ static char *mode_option;
++static int ps3fb_fb_setup(struct device *dev,
++                        u64 context_handle,
++                        struct gpu_fifo *fifo)
++{
++      /* FIFO program for L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP from LV1 */
++      const static u32 fifo_setup_program[] = {
++              0x00042000,
++              0x31337303,
++
++              0x00042180,
++              0x66604200,
++
++              0x00082184,
++              0xFEED0001,
++              0xFEED0000,
++
++              0x00044000,
++              0x3137C0DE,
++
++              0x00044180,
++              0x66604200,
++
++              0x00084184,
++              0xFEED0000,
++              0xFEED0001,
++
++              0x00046000,
++              0x313371C3,
++
++              0x00046180,
++              0x66604200,
++
++              0x00046184,
++              0xFEED0000,
++
++              0x00046188,
++              0xFEED0000,
++
++              0x0004A000,
++              0x31337808,
++
++              0x0020A180,
++              0x66604200,
++              0x00000000,
++              0x00000000,
++              0x00000000,
++              0x00000000,
++              0x00000000,
++              0x00000000,
++              0x313371C3,
++
++              0x0008A2FC,
++              0x00000003,
++              0x00000004,
++
++              0x00048000,
++              0x31337A73,
++
++              0x00048180,
++              0x66604200,
++
++              0x00048184,
++              0xFEED0000,
++
++              0x0004C000,
++              0x3137AF00,
++
++              0x0004C180,
++              0x66604200,
++
++              0x00000000,
++      };
++      int retval = 0;
++      int status;
++      volatile struct gpu_fifo_ctrl *fifo_ctrl = fifo->ctrl;
++      u32 *fifo_prev = fifo->curr;
++      unsigned int timeout;
++
++      pr_debug("%s: enter\n", __func__);
++
++      /* copy setup program to FIFO */
++      memcpy(fifo->curr, fifo_setup_program, sizeof(fifo_setup_program));
++      fifo->curr += sizeof(fifo_setup_program) / sizeof(u32);
++
++      /* set PUT and GET registers */
++      status = lv1_gpu_context_attribute(context_handle,
++                                         L1GPU_CONTEXT_ATTRIBUTE_FIFO_SETUP,
++                                         fifo->ioif,          /* PUT */
++                                         fifo->ioif,          /* GET */
++                                         0,                   /* REF */
++                                         0);
++      if (status) {
++              dev_err(dev, "%s: lv1_gpu_context_attribute failed (%d)\n",
++                      __func__, status);
++              retval = -ENXIO;
++              goto done;
++      }
++
++      /* kick FIFO */
++      fifo_ctrl->put += (fifo->curr - fifo_prev) * sizeof(u32);
++
++      /* wait until FIFO is done */
++      timeout = 100000;
++      while (timeout--) {
++              if (fifo_ctrl->put == fifo_ctrl->get)
++                      break;
++      }
++
++      if (fifo_ctrl->put != fifo_ctrl->get)
++              retval = -ETIMEDOUT;
++
++done:
++
++      pr_debug("%s: leave (%d)\n", __func__, retval);
++
++      return (retval);
++}
++
++static int ps3fb_fb_blit(struct gpu_fifo *fifo,
++                       u64 dst_offset, u64 src_offset,
++                       u32 width, u32 height,
++                       u32 dst_pitch, u32 src_pitch,
++                       u64 flags)
++{
++#define BLEN  0x400UL
++
++      int retval = 0;
++      volatile struct gpu_fifo_ctrl *fifo_ctrl = fifo->ctrl;
++      u32 *fifo_prev = fifo->curr;
++      unsigned int timeout;
++      u32 h, w, x, y, dx, dy;
++
++      pr_debug("%s: enter\n", __func__);
++
++      /* check if there is enough free space in FIFO */
++      if ((fifo->len - ((fifo->curr - fifo->start) * sizeof(u32))) < 0x1000) {
++              /* no, jump back to FIFO start */
++
++              pr_debug("%s: not enough free space left in FIFO put (0x%08x) get (0x%08x)\n",
++                      __func__, fifo_ctrl->put, fifo_ctrl->get);
++
++              *fifo->curr++ = 0x20000000 /* JMP */ | fifo->ioif;
++
++              /* kick FIFO */
++              fifo_ctrl->put = fifo->ioif;
++
++              /* wait until FIFO is done */
++              timeout = 100000;
++              while (timeout--) {
++                      if (fifo_ctrl->put == fifo_ctrl->get)
++                              break;
++              }
++
++              if (fifo_ctrl->put != fifo_ctrl->get) {
++                      retval = -ETIMEDOUT;
++                      goto done;
++              }
++
++              fifo->curr = fifo->start;
++              fifo_prev = fifo->curr;
++      }
++
++      /* FIFO program for L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT from LV1 (transfer image) */
++
++      /* set source location */
++      *fifo->curr++ = 0x0004C184;
++      *fifo->curr++ = 0xFEED0001; /* GART memory */
++
++      *fifo->curr++ = 0x0004C198;
++      *fifo->curr++ = 0x313371C3;
++
++      *fifo->curr++ = 0x00046300;
++      *fifo->curr++ = 0x0000000A; /* 4 bytes per pixel */
++
++      /*
++       * Transfer data in a block-wise fashion with block size 1024x1024x4 bytes
++       * by using RSX DMA controller. Go from top to bottom and from left to right.
++       */
++
++      h = height;
++      y = 0;
++
++      while (h) {
++              dy = (h <= BLEN) ? h : BLEN;
++
++              w = width;
++              x = 0;
++
++              while (w) {
++                      dx = (w <= BLEN) ? w : BLEN;
++
++                      *fifo->curr++ = 0x0004630C;
++                      *fifo->curr++ = dst_offset + (y & ~(BLEN - 1)) * dst_pitch + (x & ~(BLEN - 1)) * BPP; /* destination */
++
++                      *fifo->curr++ = 0x00046304;
++                      *fifo->curr++ = (dst_pitch << 16) | dst_pitch;
++
++                      *fifo->curr++ = 0x0024C2FC;
++                      *fifo->curr++ = 0x00000001;
++                      *fifo->curr++ = 0x00000003; /* 4 bytes per pixel */
++                      *fifo->curr++ = 0x00000003;
++                      *fifo->curr++ = ((x & (BLEN - 1)) << 16) | (y & (BLEN - 1));
++                      *fifo->curr++ = (dy << 16) | dx;
++                      *fifo->curr++ = ((x & (BLEN - 1)) << 16) | (y & (BLEN - 1));
++                      *fifo->curr++ = (dy << 16) | dx;
++                      *fifo->curr++ = 0x00100000;
++                      *fifo->curr++ = 0x00100000;
++
++                      *fifo->curr++ = 0x0010C400;
++                      *fifo->curr++ = (dy << 16) | ((dx < 0x10) ? 0x10 : (dx + 1) & ~0x1);
++                      *fifo->curr++ = 0x00020000 | src_pitch;
++                      *fifo->curr++ = src_offset + y * src_pitch + x * BPP; /* source */
++                      *fifo->curr++ = 0x00000000;
++
++                      w -= dx;
++                      x += dx;
++              }
++
++              h -= dy;
++              y += dy;
++      }
++
++#if 0
++      /* wait for idle  */
++      *fifo->curr++ = 0x00040110;
++      *fifo->curr++ = 0x00000000;
++#endif
++
++      /* kick FIFO */
++      fifo_ctrl->put += (fifo->curr - fifo_prev) * sizeof(u32);
++
++      /* wait until FIFO is done */
++      if (flags & L1GPU_FB_BLIT_WAIT_FOR_COMPLETION) {
++              timeout = 100000;
++              while (timeout--) {
++                      if (fifo_ctrl->put == fifo_ctrl->get)
++                              break;
++              }
++
++              if (fifo_ctrl->put != fifo_ctrl->get)
++                      retval = -ETIMEDOUT;
++      }
++
++done:
++
++      pr_debug("%s: leave (%d)\n", __func__, retval);
++
++      return (retval);
++
++#undef BLEN
++}
++
+ static int ps3fb_cmp_mode(const struct fb_videomode *vmode,
+                         const struct fb_var_screeninfo *var)
+ {
+@@ -444,24 +733,20 @@
+                            u32 src_line_length)
+ {
+       int status;
+-      u64 line_length;
+-
+-      line_length = dst_line_length;
+-      if (src_line_length != dst_line_length)
+-              line_length |= (u64)src_line_length << 32;
+       src_offset += GPU_FB_START;
+       mutex_lock(&ps3_gpu_mutex);
+-      status = lv1_gpu_fb_blit(ps3fb.context_handle, dst_offset,
+-                               GPU_IOIF + src_offset,
+-                               L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |
+-                               (width << 16) | height,
+-                               line_length);
++      status = ps3fb_fb_blit(&ps3fb.fifo,
++                             dst_offset,
++                             GPU_IOIF + src_offset,
++                             width, height,
++                             dst_line_length, src_line_length,
++                             L1GPU_FB_BLIT_WAIT_FOR_COMPLETION);
+       mutex_unlock(&ps3_gpu_mutex);
+       if (status)
+-              dev_err(dev, "%s: lv1_gpu_fb_blit failed: %d\n", __func__,
++              dev_err(dev, "%s: ps3fb_fb_blit failed: %d\n", __func__,
+                       status);
+ #ifdef HEAD_A
+       status = lv1_gpu_display_flip(ps3fb.context_handle, 0, frame_offset);
+@@ -912,6 +1197,34 @@
+       return 0;
+ }
++static void ps3fb_print_graph_exception_info(struct device *dev,
++                                           struct gpu_graph_exception_info *info)
++{
++      int i;
++
++      dev_err(dev, "channel id 0x%08x cause 0x%08x\n", info->channel_id, info->cause);
++
++      /* print FIFO info */
++
++      dev_err(dev, "fifo:\n");
++      dev_err(dev, "\tdma get 0x%08x dma put 0x%08x\n",
++              info->dma_get, info->dma_put);
++      dev_err(dev, "\tcall 0x%08x jump 0x%08x\n", info->call, info->jump);
++      dev_err(dev, "\tget 0x%08x put 0x%08x ref 0x%08x\n",
++              info->fifo_get, info->fifo_put, info->fifo_ref);
++
++      for (i = 0; i < 512; i += 4) {
++              dev_err(dev, "\t%s %s [%03x] %08x:%08x %08x:%08x %08x:%08x %08x:%08x\n",
++                      (((info->fifo_put & ~0x3) == i) ? "P" : " "),
++                      (((info->fifo_get & ~0x3) == i) ? "G" : " "),
++                      i,
++                      info->fifo_cache[i * 4 + 0], info->graph_fifo[i * 4 + 0],
++                      info->fifo_cache[i * 4 + 1], info->graph_fifo[i * 4 + 1],
++                      info->fifo_cache[i * 4 + 2], info->graph_fifo[i * 4 + 2],
++                      info->fifo_cache[i * 4 + 3], info->graph_fifo[i * 4 + 3]);
++      }
++}
++
+ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr)
+ {
+       struct device *dev = ptr;
+@@ -926,6 +1239,11 @@
+               return IRQ_NONE;
+       }
++      if (v1 & (1 << GPU_INTR_STATUS_GRAPH_EXCEPTION)) {
++              dev_err(dev, "%s: graphics exception\n", __func__);
++              ps3fb_print_graph_exception_info(dev, &ps3fb.dinfo->irq.graph_exception_info);
++      }
++
+       if (v1 & (1 << GPU_INTR_STATUS_VSYNC_1)) {
+               /* VSYNC */
+               ps3fb.vblank_count = head->vblank_count;
+@@ -1030,8 +1348,9 @@
+       }
+       /* get gpu context handle */
+-      status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, 0, 0, 0, 0,
+-                                       &ps3fb.memory_handle, &ddr_lpar);
++      status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, ps3fb_gpu_mem_size[0],
++              ps3fb_gpu_mem_size[1], ps3fb_gpu_mem_size[2], ps3fb_gpu_mem_size[3],
++              &ps3fb.memory_handle, &ddr_lpar);
+       if (status) {
+               dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n",
+                       __func__, status);
+@@ -1039,7 +1358,7 @@
+       }
+       dev_dbg(&dev->core, "ddr:lpar:0x%llx\n", ddr_lpar);
+-      status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0,
++      status = lv1_gpu_context_allocate(ps3fb.memory_handle, ps3fb_gpu_ctx_flags,
+                                         &ps3fb.context_handle,
+                                         &lpar_dma_control, &lpar_driver_info,
+                                         &lpar_reports, &lpar_reports_size);
+@@ -1089,7 +1408,8 @@
+               goto err_destroy_plug;
+       }
+-      dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) |
++      dinfo->irq.mask = (1 << GPU_INTR_STATUS_GRAPH_EXCEPTION) |
++                        (1 << GPU_INTR_STATUS_VSYNC_1) |
+                         (1 << GPU_INTR_STATUS_FLIP_1);
+       /* Clear memory to prevent kernel info leakage into userspace */
+@@ -1434,19 +1434,30 @@
+               ps3fb_videomemory.address, GPU_IOIF, xdr_lpar,
+               ps3fb_videomemory.size);
+-      status = lv1_gpu_fb_setup(ps3fb.context_handle, xdr_lpar,
+-                                GPU_CMD_BUF_SIZE, GPU_IOIF);
++      /* FIFO control */
++      ps3fb.fifo.ctrl = (void __force *)ioremap_nocache(lpar_dma_control, PAGE_SIZE);
++      if (!ps3fb.fifo.ctrl) {
++              dev_err(&dev->core, "%s: ioremap_nocache failed\n", __func__);
++              goto err_context_unmap;
++      }
++
++      ps3fb.fifo.start = ps3fb_videomemory.address;
++      ps3fb.fifo.curr = ps3fb.fifo.start;
++      ps3fb.fifo.len = GPU_FB_START;
++      ps3fb.fifo.ioif = GPU_IOIF;
++
++      status = ps3fb_fb_setup(&dev->core, ps3fb.context_handle, &ps3fb.fifo);
+       if (status) {
+-              dev_err(&dev->core, "%s: lv1_gpu_fb_setup failed: %d\n",
++              dev_err(&dev->core, "%s: ps3fb_fb_setup failed: %d\n",
+                       __func__, status);
+-              retval = -ENXIO;
+-              goto err_context_unmap;
++              retval = status;
++              goto err_iounmap_fifo_ctrl;
+       }
+       info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core);
+       if (!info) {
+               retval = -ENOMEM;
+-              goto err_context_fb_close;
++              goto err_iounmap_fifo_ctrl;
+       }
+       par = info->par;
+@@ -1188,8 +1519,8 @@
+       fb_dealloc_cmap(&info->cmap);
+ err_framebuffer_release:
+       framebuffer_release(info);
+-err_context_fb_close:
+-      lv1_gpu_fb_close(ps3fb.context_handle);
++err_iounmap_fifo_ctrl:
++      iounmap((u8 __force __iomem *)ps3fb.fifo.ctrl);
+ err_context_unmap:
+       lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar,
+                             ps3fb_videomemory.size, CBE_IOPTE_M);
+@@ -1235,7 +1566,7 @@
+               ps3_system_bus_set_drvdata(dev, NULL);
+       }
+       iounmap((u8 __force __iomem *)ps3fb.dinfo);
+-      lv1_gpu_fb_close(ps3fb.context_handle);
++      iounmap((u8 __force __iomem *)ps3fb.fifo.ctrl);
+       lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar,
+                             ps3fb_videomemory.size, CBE_IOPTE_M);
+       lv1_gpu_context_free(ps3fb.context_handle);
diff --git a/0030-ps3flash.patch b/0030-ps3flash.patch
new file mode 100644 (file)
index 0000000..81eee59
--- /dev/null
@@ -0,0 +1,1076 @@
+--- a/arch/powerpc/include/asm/ps3.h   2012-01-24 18:09:05.452290538 +0100
++++ b/arch/powerpc/include/asm/ps3.h   2012-01-24 18:37:24.181894425 +0100
+@@ -326,6 +326,7 @@
+       PS3_MATCH_ID_SOUND              = 9,
+       PS3_MATCH_ID_GPU                = 10,
+       PS3_MATCH_ID_LPM                = 11,
++      PS3_MATCH_ID_STOR_NOR_FLASH     = 12,
+ };
+ enum ps3_match_sub_id {
+@@ -345,6 +346,7 @@
+ #define PS3_MODULE_ALIAS_GPU_FB               "ps3:10:1"
+ #define PS3_MODULE_ALIAS_GPU_RAMDISK  "ps3:10:2"
+ #define PS3_MODULE_ALIAS_LPM          "ps3:11:0"
++#define PS3_MODULE_ALIAS_STOR_NOR_FLASH       "ps3:12:0"
+ enum ps3_system_bus_device_type {
+       PS3_DEVICE_TYPE_IOC0 = 1,
+--- a/arch/powerpc/platforms/ps3/platform.h    2012-01-12 20:42:45.000000000 +0100
++++ b/arch/powerpc/platforms/ps3/platform.h    2012-01-24 18:36:02.470631575 +0100
+@@ -88,6 +88,7 @@
+       PS3_DEV_TYPE_STOR_ROM = TYPE_ROM,       /* 5 */
+       PS3_DEV_TYPE_SB_GPIO = 6,
+       PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC,     /* 14 */
++      PS3_DEV_TYPE_STOR_NOR_FLASH = 254,
+ };
+ int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
+--- a/arch/powerpc/platforms/ps3/device-init.c 2012-01-12 20:42:45.000000000 +0100
++++ b/arch/powerpc/platforms/ps3/device-init.c 2012-01-24 18:34:44.089470755 +0100
+@@ -592,6 +592,13 @@
+                                __func__, __LINE__);
+               break;
++      case PS3_DEV_TYPE_STOR_NOR_FLASH:
++              result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_NOR_FLASH);
++              if (result)
++                      pr_debug("%s:%u ps3_setup_storage_dev failed\n",
++                               __func__, __LINE__);
++              break;
++
+       default:
+               result = 0;
+               pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
+--- a/arch/powerpc/platforms/ps3/system-bus.c  2012-01-12 20:42:45.000000000 +0100
++++ b/arch/powerpc/platforms/ps3/system-bus.c  2012-01-24 19:25:53.073547395 +0100
+@@ -174,6 +174,7 @@
+       case PS3_MATCH_ID_STOR_DISK:
+       case PS3_MATCH_ID_STOR_ROM:
+       case PS3_MATCH_ID_STOR_FLASH:
++      case PS3_MATCH_ID_STOR_NOR_FLASH:
+               return ps3_open_hv_device_sb(dev);
+       case PS3_MATCH_ID_SOUND:
+@@ -212,6 +213,7 @@
+       case PS3_MATCH_ID_STOR_DISK:
+       case PS3_MATCH_ID_STOR_ROM:
+       case PS3_MATCH_ID_STOR_FLASH:
++      case PS3_MATCH_ID_STOR_NOR_FLASH:
+               return ps3_close_hv_device_sb(dev);
+       case PS3_MATCH_ID_SOUND:
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-01-24 19:02:19.221491270 +0100
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-01-24 19:03:21.419302797 +0100
+@@ -128,6 +128,26 @@
+         be disabled on the kernel command line using "ps3flash=off", to
+         not allocate this fixed buffer.
++config PS3_FLASH_NG
++      tristate "PS3 Flash Storage Driver"
++      depends on PPC_PS3 && BLOCK && PS3_FLASH!=y && PS3_FLASH!=m
++      select PS3_STORAGE
++      help
++        Include support for the PS3 Flash Storage.
++
++        This support is required to access the PS3 flash.
++        In general, all users will say Y or M.
++
++config PS3_NOR_FLASH
++      tristate "PS3 NOR Flash Storage Driver"
++      depends on PPC_PS3 && BLOCK
++      select PS3_STORAGE
++      help
++        Include support for the PS3 NOR Flash Storage.
++
++        This support is required to access the PS3 NOR flash.
++        In general, all users will say Y or M.
++
+ config PS3_VRAM
+       tristate "PS3 Video RAM Storage Driver"
+       depends on FB_PS3=y && BLOCK && m
+--- a/drivers/block/Makefile   2012-01-24 19:02:39.908639282 +0100
++++ b/drivers/block/Makefile   2012-01-24 18:32:56.294515362 +0100
+@@ -10,6 +10,8 @@
+ obj-$(CONFIG_BLK_DEV_FD)      += floppy.o
+ obj-$(CONFIG_AMIGA_FLOPPY)    += amiflop.o
+ obj-$(CONFIG_PS3_DISK)                += ps3disk.o
++obj-$(CONFIG_PS3_FLASH_NG)    += ps3flash.o
++obj-$(CONFIG_PS3_NOR_FLASH)   += ps3nflash.o
+ obj-$(CONFIG_PS3_VRAM)                += ps3vram.o
+ obj-$(CONFIG_ATARI_FLOPPY)    += ataflop.o
+ obj-$(CONFIG_AMIGA_Z2RAM)     += z2ram.o
+--- /dev/null  2012-11-28 15:48:49.557690341 +0100
++++ b/drivers/block/ps3flash.c 2013-02-14 11:18:29.076533279 +0100
+@@ -0,0 +1,520 @@
++/*
++ * PS3 Flash Storage Driver
++ *
++ * Copyright (C) 2007 Sony Computer Entertainment Inc.
++ * Copyright 2007 Sony Corp.
++ * Copyright (C) 2011 graf_chokolo <[email protected]>.
++ * Copyright (C) 2011-2013 glevand <[email protected]>.
++ *
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/blkdev.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++
++#include <asm/lv1call.h>
++#include <asm/ps3stor.h>
++#include <asm/firmware.h>
++
++
++#define DEVICE_NAME           "ps3flash"
++
++#define BOUNCE_SIZE           (64*1024)
++
++#define PS3FLASH_MAX_NUM_REGS 8
++
++#define PS3FLASH_MINORS               16
++
++
++#define PS3FLASH_NAME         "ps3flash%c"
++
++
++struct ps3flash_private {
++      spinlock_t lock;                /* Request queue spinlock */
++      unsigned int blocking_factor;
++      struct request *req;
++      u64 raw_capacity;
++      int is_vflash;
++      struct gendisk *gendisk[PS3FLASH_MAX_NUM_REGS];
++      struct request_queue *queue[PS3FLASH_MAX_NUM_REGS];
++      int next_queue;
++};
++
++
++#define LV1_STORAGE_ATA_FLUSH_CACHE_EXT       (0x31)
++
++
++static int ps3flash_major;
++
++
++static const struct block_device_operations ps3flash_fops = {
++      .owner          = THIS_MODULE,
++};
++
++static unsigned int region_flags[] =
++{
++      0x6, 0x2, 0x4, 0x4, 0x4, 0x0, 0x2, 0x0,
++};
++module_param_array(region_flags, uint, NULL, S_IRUGO);
++MODULE_PARM_DESC(region_flags, "Region flags");
++
++
++static void ps3flash_scatter_gather(struct ps3_storage_device *dev,
++                                 struct request *req, int gather)
++{
++      unsigned int offset = 0;
++      struct req_iterator iter;
++      struct bio_vec *bvec;
++      unsigned int i = 0;
++      size_t size;
++      void *buf;
++
++      rq_for_each_segment(bvec, req, iter) {
++              unsigned long flags;
++              dev_dbg(&dev->sbd.core,
++                      "%s:%u: bio %u: %u segs %u sectors from %lu\n",
++                      __func__, __LINE__, i, bio_segments(iter.bio),
++                      bio_sectors(iter.bio), iter.bio->bi_sector);
++
++              size = bvec->bv_len;
++              buf = bvec_kmap_irq(bvec, &flags);
++              if (gather)
++                      memcpy(dev->bounce_buf+offset, buf, size);
++              else
++                      memcpy(buf, dev->bounce_buf+offset, size);
++              offset += size;
++              flush_kernel_dcache_page(bvec->bv_page);
++              bvec_kunmap_irq(buf, &flags);
++              i++;
++      }
++}
++
++static int ps3flash_submit_request_sg(struct ps3_storage_device *dev,
++                                   struct request *req)
++{
++      struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      int write = rq_data_dir(req), res;
++      const char *op = write ? "write" : "read";
++      u64 start_sector, sectors;
++      unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) / PS3FLASH_MINORS;
++      unsigned int region_id = dev->regions[region_idx].id;
++      unsigned int region_flags = dev->regions[region_idx].flags;
++
++#ifdef DEBUG
++      unsigned int n = 0;
++      struct bio_vec *bv;
++      struct req_iterator iter;
++
++      rq_for_each_segment(bv, req, iter)
++              n++;
++      dev_dbg(&dev->sbd.core,
++              "%s:%u: %s req has %u bvecs for %u sectors\n",
++              __func__, __LINE__, op, n, blk_rq_sectors(req));
++#endif
++
++      start_sector = blk_rq_pos(req) * priv->blocking_factor;
++      sectors = blk_rq_sectors(req) * priv->blocking_factor;
++      dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
++              __func__, __LINE__, op, sectors, start_sector);
++
++      if (write) {
++              ps3flash_scatter_gather(dev, req, 1);
++
++              res = lv1_storage_write(dev->sbd.dev_id, region_id,
++                                      start_sector, sectors, region_flags,
++                                      dev->bounce_lpar, &dev->tag);
++      } else {
++              res = lv1_storage_read(dev->sbd.dev_id, region_id,
++                                     start_sector, sectors, region_flags,
++                                     dev->bounce_lpar, &dev->tag);
++      }
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
++                      __LINE__, op, res);
++              __blk_end_request_all(req, -EIO);
++              return 0;
++      }
++
++      priv->req = req;
++      return 1;
++}
++
++static int ps3flash_submit_flush_request(struct ps3_storage_device *dev,
++                                      struct request *req)
++{
++      struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      u64 res;
++
++      dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
++
++      res = lv1_storage_send_device_command(dev->sbd.dev_id,
++                                            LV1_STORAGE_ATA_FLUSH_CACHE_EXT, 0, 0, 0,
++                                            0, &dev->tag);
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n",
++                      __func__, __LINE__, res);
++              __blk_end_request_all(req, -EIO);
++              return 0;
++      }
++
++      priv->req = req;
++      return 1;
++}
++
++static void ps3flash_do_request(struct ps3_storage_device *dev,
++                             struct request_queue *q)
++{
++      struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      struct request *req;
++
++      dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
++
++      while ((req = blk_fetch_request(q))) {
++              if (priv->is_vflash && (req->cmd_flags & REQ_FLUSH)) {
++                      if (ps3flash_submit_flush_request(dev, req))
++                              break;
++              } else if (req->cmd_type == REQ_TYPE_FS) {
++                      if (ps3flash_submit_request_sg(dev, req))
++                              break;
++              } else {
++                      blk_dump_rq_flags(req, DEVICE_NAME " bad request");
++                      __blk_end_request_all(req, -EIO);
++                      continue;
++              }
++      }
++}
++
++static void ps3flash_request(struct request_queue *q)
++{
++      struct ps3_storage_device *dev = q->queuedata;
++      struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++
++      if (priv->req) {
++              dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
++              return;
++      }
++
++      ps3flash_do_request(dev, q);
++}
++
++static irqreturn_t ps3flash_interrupt(int irq, void *data)
++{
++      struct ps3_storage_device *dev = data;
++      struct ps3flash_private *priv;
++      struct request *req;
++      int res, read, error;
++      u64 tag, status;
++      const char *op;
++      struct request_queue *q;
++      int old_queue;
++
++      res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
++
++      if (tag != dev->tag)
++              dev_err(&dev->sbd.core,
++                      "%s:%u: tag mismatch, got %llx, expected %llx\n",
++                      __func__, __LINE__, tag, dev->tag);
++
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
++                      __func__, __LINE__, res, status);
++              return IRQ_HANDLED;
++      }
++
++      priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      req = priv->req;
++      if (!req) {
++              dev_dbg(&dev->sbd.core,
++                      "%s:%u non-block layer request completed\n", __func__,
++                      __LINE__);
++              dev->lv1_status = status;
++              complete(&dev->done);
++              return IRQ_HANDLED;
++      }
++
++      if (req->cmd_flags & REQ_FLUSH) {
++              read = 0;
++              op = "flush";
++      } else {
++              read = !rq_data_dir(req);
++              op = read ? "read" : "write";
++      }
++      if (status) {
++              dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
++                      __LINE__, op, status);
++              error = -EIO;
++      } else {
++              dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
++                      __LINE__, op);
++              error = 0;
++              if (read)
++                      ps3flash_scatter_gather(dev, req, 0);
++      }
++
++      spin_lock(&priv->lock);
++      __blk_end_request_all(req, error);
++      priv->req = NULL;
++      old_queue = priv->next_queue;
++      do {
++              q = priv->queue[priv->next_queue];
++
++              priv->next_queue++;
++              if (priv->next_queue >= dev->num_regions)
++                      priv->next_queue = 0;
++
++              if (q) {
++                      ps3flash_do_request(dev, q);
++                      if (priv->req)
++                              break;
++              }
++      } while (old_queue != priv->next_queue);
++      spin_unlock(&priv->lock);
++
++      return IRQ_HANDLED;
++}
++
++static int ps3flash_sync_cache(struct ps3_storage_device *dev)
++{
++      u64 res;
++
++      dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__);
++
++      res = ps3stor_send_command(dev, LV1_STORAGE_ATA_FLUSH_CACHE_EXT, 0, 0, 0, 0);
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n",
++                      __func__, __LINE__, res);
++              return -EIO;
++      }
++      return 0;
++}
++
++static int ps3flash_probe(struct ps3_system_bus_device *_dev)
++{
++      struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
++      struct ps3flash_private *priv;
++      int error;
++      unsigned int regidx, devidx;
++      u64 lpar_id, flash_ext_flag, junk;
++      struct request_queue *queue;
++      struct gendisk *gendisk;
++
++      BUG_ON(dev->num_regions > PS3FLASH_MAX_NUM_REGS);
++
++      if (dev->blk_size < 512) {
++              dev_err(&dev->sbd.core,
++                      "%s:%u: cannot handle block size %llu\n", __func__,
++                      __LINE__, dev->blk_size);
++              return -EINVAL;
++      }
++
++      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++      if (!priv) {
++              error = -ENOMEM;
++              goto fail;
++      }
++
++      ps3_system_bus_set_drvdata(_dev, priv);
++      spin_lock_init(&priv->lock);
++
++      dev->bounce_size = BOUNCE_SIZE;
++      dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
++      if (!dev->bounce_buf) {
++              error = -ENOMEM;
++              goto fail_free_priv;
++      }
++
++      for (regidx = 0; regidx < dev->num_regions; regidx++)
++              dev->regions[regidx].flags = region_flags[regidx];
++
++      error = ps3stor_setup(dev, ps3flash_interrupt);
++      if (error)
++              goto fail_free_bounce;
++
++      priv->raw_capacity = dev->regions[0].size;
++
++      error = lv1_get_logical_partition_id(&lpar_id);
++      if (error)
++              goto fail_teardown;
++
++      error = lv1_read_repository_node(1, 0x0000000073797300ul /* sys */,
++                                          0x666c617368000000ul /* flash */,
++                                          0x6578740000000000ul /* ext */,
++                                          0, &flash_ext_flag, &junk);
++      if (error)
++              goto fail_teardown;
++
++      priv->is_vflash = !(flash_ext_flag & 0x1);
++
++      dev_info(&dev->sbd.core, "VFLASH is %s\n",
++               priv->is_vflash ? "on" : "off");
++
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (test_bit(devidx, &dev->accessible_regions) == 0)
++                      continue;
++
++              queue = blk_init_queue(ps3flash_request, &priv->lock);
++              if (!queue) {
++                      dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
++                              __func__, __LINE__);
++                      error = -ENOMEM;
++                      goto fail_cleanup;
++              }
++
++              priv->queue[devidx] = queue;
++              queue->queuedata = dev;
++
++              blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
++
++              blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
++              blk_queue_segment_boundary(queue, -1UL);
++              blk_queue_dma_alignment(queue, dev->blk_size-1);
++              blk_queue_logical_block_size(queue, dev->blk_size);
++
++              blk_queue_flush(queue, REQ_FLUSH);
++
++              blk_queue_max_segments(queue, -1);
++              blk_queue_max_segment_size(queue, dev->bounce_size);
++
++              gendisk = alloc_disk(PS3FLASH_MINORS);
++              if (!gendisk) {
++                      dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
++                              __LINE__);
++                      error = -ENOMEM;
++                      goto fail_cleanup;
++              }
++
++              priv->gendisk[devidx] = gendisk;
++              gendisk->major = ps3flash_major;
++              gendisk->first_minor = devidx * PS3FLASH_MINORS;
++              gendisk->fops = &ps3flash_fops;
++              gendisk->queue = queue;
++              gendisk->private_data = dev;
++              gendisk->driverfs_dev = &dev->sbd.core;
++              snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3FLASH_NAME,
++                       devidx+'a');
++              priv->blocking_factor = dev->blk_size >> 9;
++              set_capacity(gendisk,
++                       dev->regions[devidx].size*priv->blocking_factor);
++
++              dev_info(&dev->sbd.core,
++                       "%s (%llu MiB total, %lu MiB region)\n",
++                       gendisk->disk_name, priv->raw_capacity >> 11,
++                       get_capacity(gendisk) >> 11);
++
++              add_disk(gendisk);
++      }
++
++      return 0;
++
++fail_cleanup:
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (priv->gendisk[devidx]) {
++                      del_gendisk(priv->gendisk[devidx]);
++                      put_disk(priv->gendisk[devidx]);
++              }
++
++              if (priv->queue[devidx])
++                      blk_cleanup_queue(priv->queue[devidx]);
++      }
++fail_teardown:
++      ps3stor_teardown(dev);
++fail_free_bounce:
++      kfree(dev->bounce_buf);
++fail_free_priv:
++      kfree(priv);
++      ps3_system_bus_set_drvdata(_dev, NULL);
++fail:
++      return error;
++}
++
++static int ps3flash_remove(struct ps3_system_bus_device *_dev)
++{
++      struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
++      struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      unsigned int devidx;
++
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (priv->gendisk[devidx]) {
++                      del_gendisk(priv->gendisk[devidx]);
++                      put_disk(priv->gendisk[devidx]);
++              }
++
++              if (priv->queue[devidx])
++                      blk_cleanup_queue(priv->queue[devidx]);
++      }
++
++      if (priv->is_vflash) {
++              dev_notice(&dev->sbd.core, "Synchronizing disk cache\n");
++              ps3flash_sync_cache(dev);
++      }
++
++      ps3stor_teardown(dev);
++      kfree(dev->bounce_buf);
++      kfree(priv);
++      ps3_system_bus_set_drvdata(_dev, NULL);
++      return 0;
++}
++
++static struct ps3_system_bus_driver ps3flash = {
++      .match_id       = PS3_MATCH_ID_STOR_FLASH,
++      .core.name      = DEVICE_NAME,
++      .core.owner     = THIS_MODULE,
++      .probe          = ps3flash_probe,
++      .remove         = ps3flash_remove,
++      .shutdown       = ps3flash_remove,
++};
++
++
++static int __init ps3flash_init(void)
++{
++      int error;
++
++      if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
++              return -ENODEV;
++
++      error = register_blkdev(0, DEVICE_NAME);
++      if (error <= 0) {
++              printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
++                     __LINE__, error);
++              return error;
++      }
++      ps3flash_major = error;
++
++      pr_info("%s:%u: registered block device major %d\n", __func__,
++              __LINE__, ps3flash_major);
++
++      error = ps3_system_bus_driver_register(&ps3flash);
++      if (error)
++              unregister_blkdev(ps3flash_major, DEVICE_NAME);
++
++      return error;
++}
++
++static void __exit ps3flash_exit(void)
++{
++      ps3_system_bus_driver_unregister(&ps3flash);
++      unregister_blkdev(ps3flash_major, DEVICE_NAME);
++}
++
++module_init(ps3flash_init);
++module_exit(ps3flash_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("PS3 Flash Storage Driver");
++MODULE_AUTHOR("Sony Corporation");
++MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH);
+--- /dev/null  2013-02-15 18:37:48.373100583 +0100
++++ b/drivers/block/ps3nflash.c        2013-02-15 23:15:44.710737431 +0100
+@@ -0,0 +1,448 @@
++/*
++ * PS3 Flash Storage Driver
++ *
++ * Copyright (C) 2007 Sony Computer Entertainment Inc.
++ * Copyright 2007 Sony Corp.
++ * Copyright (C) 2011 graf_chokolo <[email protected]>.
++ * Copyright (C) 2011-2013 glevand <[email protected]>.
++ *
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/blkdev.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++
++#include <asm/lv1call.h>
++#include <asm/ps3stor.h>
++#include <asm/firmware.h>
++
++
++#define DEVICE_NAME           "ps3nflash"
++
++#define BOUNCE_SIZE           (64*1024)
++
++#define PS3NFLASH_MAX_NUM_REGS        8
++
++#define PS3NFLASH_MINORS      16
++
++
++#define PS3NFLASH_NAME                "ps3nflash%c"
++
++
++struct ps3nflash_private {
++      spinlock_t lock;                /* Request queue spinlock */
++      unsigned int blocking_factor;
++      struct request *req;
++      u64 raw_capacity;
++      struct gendisk *gendisk[PS3NFLASH_MAX_NUM_REGS];
++      struct request_queue *queue[PS3NFLASH_MAX_NUM_REGS];
++      int next_queue;
++};
++
++
++static int ps3nflash_major;
++
++
++static const struct block_device_operations ps3nflash_fops = {
++      .owner          = THIS_MODULE,
++};
++
++static unsigned int region_flags[] =
++{
++      0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0,
++};
++module_param_array(region_flags, uint, NULL, S_IRUGO);
++MODULE_PARM_DESC(region_flags, "Region flags");
++
++
++static void ps3nflash_scatter_gather(struct ps3_storage_device *dev,
++                                 struct request *req, int gather)
++{
++      unsigned int offset = 0;
++      struct req_iterator iter;
++      struct bio_vec *bvec;
++      unsigned int i = 0;
++      size_t size;
++      void *buf;
++
++      rq_for_each_segment(bvec, req, iter) {
++              unsigned long flags;
++              dev_dbg(&dev->sbd.core,
++                      "%s:%u: bio %u: %u segs %u sectors from %lu\n",
++                      __func__, __LINE__, i, bio_segments(iter.bio),
++                      bio_sectors(iter.bio), iter.bio->bi_sector);
++
++              size = bvec->bv_len;
++              buf = bvec_kmap_irq(bvec, &flags);
++              if (gather)
++                      memcpy(dev->bounce_buf+offset, buf, size);
++              else
++                      memcpy(buf, dev->bounce_buf+offset, size);
++              offset += size;
++              flush_kernel_dcache_page(bvec->bv_page);
++              bvec_kunmap_irq(buf, &flags);
++              i++;
++      }
++}
++
++static int ps3nflash_submit_request_sg(struct ps3_storage_device *dev,
++                                   struct request *req)
++{
++      struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      int write = rq_data_dir(req), res;
++      const char *op = write ? "write" : "read";
++      u64 start_sector, sectors;
++      unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) / PS3NFLASH_MINORS;
++      unsigned int region_id = dev->regions[region_idx].id;
++      unsigned int region_flags = dev->regions[region_idx].flags;
++
++#ifdef DEBUG
++      unsigned int n = 0;
++      struct bio_vec *bv;
++      struct req_iterator iter;
++
++      rq_for_each_segment(bv, req, iter)
++              n++;
++      dev_dbg(&dev->sbd.core,
++              "%s:%u: %s req has %u bvecs for %u sectors\n",
++              __func__, __LINE__, op, n, blk_rq_sectors(req));
++#endif
++
++      start_sector = blk_rq_pos(req) * priv->blocking_factor;
++      sectors = blk_rq_sectors(req) * priv->blocking_factor;
++      dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
++              __func__, __LINE__, op, sectors, start_sector);
++
++      if (write) {
++              ps3nflash_scatter_gather(dev, req, 1);
++
++              res = lv1_storage_write(dev->sbd.dev_id, region_id,
++                                      start_sector, sectors, region_flags,
++                                      dev->bounce_lpar, &dev->tag);
++      } else {
++              res = lv1_storage_read(dev->sbd.dev_id, region_id,
++                                     start_sector, sectors, region_flags,
++                                     dev->bounce_lpar, &dev->tag);
++      }
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
++                      __LINE__, op, res);
++              __blk_end_request_all(req, -EIO);
++              return 0;
++      }
++
++      priv->req = req;
++      return 1;
++}
++
++static void ps3nflash_do_request(struct ps3_storage_device *dev,
++                             struct request_queue *q)
++{
++      struct request *req;
++
++      dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
++
++      while ((req = blk_fetch_request(q))) {
++              if (req->cmd_type == REQ_TYPE_FS) {
++                      if (ps3nflash_submit_request_sg(dev, req))
++                              break;
++              } else {
++                      blk_dump_rq_flags(req, DEVICE_NAME " bad request");
++                      __blk_end_request_all(req, -EIO);
++                      continue;
++              }
++      }
++}
++
++static void ps3nflash_request(struct request_queue *q)
++{
++      struct ps3_storage_device *dev = q->queuedata;
++      struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++
++      if (priv->req) {
++              dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
++              return;
++      }
++
++      ps3nflash_do_request(dev, q);
++}
++
++static irqreturn_t ps3nflash_interrupt(int irq, void *data)
++{
++      struct ps3_storage_device *dev = data;
++      struct ps3nflash_private *priv;
++      struct request *req;
++      int res, read, error;
++      u64 tag, status;
++      const char *op;
++      struct request_queue *q;
++      int old_queue;
++
++      res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
++
++      if (tag != dev->tag)
++              dev_err(&dev->sbd.core,
++                      "%s:%u: tag mismatch, got %llx, expected %llx\n",
++                      __func__, __LINE__, tag, dev->tag);
++
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
++                      __func__, __LINE__, res, status);
++              return IRQ_HANDLED;
++      }
++
++      priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      req = priv->req;
++      if (!req) {
++              dev_dbg(&dev->sbd.core,
++                      "%s:%u non-block layer request completed\n", __func__,
++                      __LINE__);
++              dev->lv1_status = status;
++              complete(&dev->done);
++              return IRQ_HANDLED;
++      }
++
++      read = !rq_data_dir(req);
++      op = read ? "read" : "write";
++
++      if (status) {
++              dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
++                      __LINE__, op, status);
++              error = -EIO;
++      } else {
++              dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
++                      __LINE__, op);
++              error = 0;
++              if (read)
++                      ps3nflash_scatter_gather(dev, req, 0);
++      }
++
++      spin_lock(&priv->lock);
++      __blk_end_request_all(req, error);
++      priv->req = NULL;
++      old_queue = priv->next_queue;
++      do {
++              q = priv->queue[priv->next_queue];
++
++              priv->next_queue++;
++              if (priv->next_queue >= dev->num_regions)
++                      priv->next_queue = 0;
++
++              if (q) {
++                      ps3nflash_do_request(dev, q);
++                      if (priv->req)
++                              break;
++              }
++      } while (old_queue != priv->next_queue);
++      spin_unlock(&priv->lock);
++
++      return IRQ_HANDLED;
++}
++
++static int ps3nflash_probe(struct ps3_system_bus_device *_dev)
++{
++      struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
++      struct ps3nflash_private *priv;
++      int error;
++      unsigned int regidx, devidx;
++      struct request_queue *queue;
++      struct gendisk *gendisk;
++
++      BUG_ON(dev->num_regions > PS3NFLASH_MAX_NUM_REGS);
++
++      if (dev->blk_size < 512) {
++              dev_err(&dev->sbd.core,
++                      "%s:%u: cannot handle block size %llu\n", __func__,
++                      __LINE__, dev->blk_size);
++              return -EINVAL;
++      }
++
++      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++      if (!priv) {
++              error = -ENOMEM;
++              goto fail;
++      }
++
++      ps3_system_bus_set_drvdata(_dev, priv);
++      spin_lock_init(&priv->lock);
++
++      dev->bounce_size = BOUNCE_SIZE;
++      dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
++      if (!dev->bounce_buf) {
++              error = -ENOMEM;
++              goto fail_free_priv;
++      }
++
++      for (regidx = 0; regidx < dev->num_regions; regidx++)
++              dev->regions[regidx].flags = region_flags[regidx];
++
++      error = ps3stor_setup(dev, ps3nflash_interrupt);
++      if (error)
++              goto fail_free_bounce;
++
++      priv->raw_capacity = dev->regions[0].size;
++
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (test_bit(devidx, &dev->accessible_regions) == 0)
++                      continue;
++
++              queue = blk_init_queue(ps3nflash_request, &priv->lock);
++              if (!queue) {
++                      dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
++                              __func__, __LINE__);
++                      error = -ENOMEM;
++                      goto fail_cleanup;
++              }
++
++              priv->queue[devidx] = queue;
++              queue->queuedata = dev;
++
++              blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
++
++              blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
++              blk_queue_segment_boundary(queue, -1UL);
++              blk_queue_dma_alignment(queue, dev->blk_size-1);
++              blk_queue_logical_block_size(queue, dev->blk_size);
++
++              blk_queue_flush(queue, REQ_FLUSH);
++
++              blk_queue_max_segments(queue, -1);
++              blk_queue_max_segment_size(queue, dev->bounce_size);
++
++              gendisk = alloc_disk(PS3NFLASH_MINORS);
++              if (!gendisk) {
++                      dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
++                              __LINE__);
++                      error = -ENOMEM;
++                      goto fail_cleanup;
++              }
++
++              priv->gendisk[devidx] = gendisk;
++              gendisk->major = ps3nflash_major;
++              gendisk->first_minor = devidx * PS3NFLASH_MINORS;
++              gendisk->fops = &ps3nflash_fops;
++              gendisk->queue = queue;
++              gendisk->private_data = dev;
++              gendisk->driverfs_dev = &dev->sbd.core;
++              snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3NFLASH_NAME,
++                       devidx+'a');
++              priv->blocking_factor = dev->blk_size >> 9;
++              set_capacity(gendisk,
++                       dev->regions[devidx].size*priv->blocking_factor);
++
++              dev_info(&dev->sbd.core,
++                       "%s (%llu MiB total, %lu MiB region)\n",
++                       gendisk->disk_name, priv->raw_capacity >> 11,
++                       get_capacity(gendisk) >> 11);
++
++              add_disk(gendisk);
++      }
++
++      return 0;
++
++fail_cleanup:
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (priv->gendisk[devidx]) {
++                      del_gendisk(priv->gendisk[devidx]);
++                      put_disk(priv->gendisk[devidx]);
++              }
++
++              if (priv->queue[devidx])
++                      blk_cleanup_queue(priv->queue[devidx]);
++      }
++      ps3stor_teardown(dev);
++fail_free_bounce:
++      kfree(dev->bounce_buf);
++fail_free_priv:
++      kfree(priv);
++      ps3_system_bus_set_drvdata(_dev, NULL);
++fail:
++      return error;
++}
++
++static int ps3nflash_remove(struct ps3_system_bus_device *_dev)
++{
++      struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
++      struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++      unsigned int devidx;
++
++      for (devidx = 0; devidx < dev->num_regions; devidx++)
++      {
++              if (priv->gendisk[devidx]) {
++                      del_gendisk(priv->gendisk[devidx]);
++                      put_disk(priv->gendisk[devidx]);
++              }
++
++              if (priv->queue[devidx])
++                      blk_cleanup_queue(priv->queue[devidx]);
++      }
++
++      ps3stor_teardown(dev);
++      kfree(dev->bounce_buf);
++      kfree(priv);
++      ps3_system_bus_set_drvdata(_dev, NULL);
++      return 0;
++}
++
++static struct ps3_system_bus_driver ps3nflash = {
++      .match_id       = PS3_MATCH_ID_STOR_NOR_FLASH,
++      .core.name      = DEVICE_NAME,
++      .core.owner     = THIS_MODULE,
++      .probe          = ps3nflash_probe,
++      .remove         = ps3nflash_remove,
++      .shutdown       = ps3nflash_remove,
++};
++
++
++static int __init ps3nflash_init(void)
++{
++      int error;
++
++      if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
++              return -ENODEV;
++
++      error = register_blkdev(0, DEVICE_NAME);
++      if (error <= 0) {
++              printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
++                     __LINE__, error);
++              return error;
++      }
++      ps3nflash_major = error;
++
++      pr_info("%s:%u: registered block device major %d\n", __func__,
++              __LINE__, ps3nflash_major);
++
++      error = ps3_system_bus_driver_register(&ps3nflash);
++      if (error)
++              unregister_blkdev(ps3nflash_major, DEVICE_NAME);
++
++      return error;
++}
++
++static void __exit ps3nflash_exit(void)
++{
++      ps3_system_bus_driver_unregister(&ps3nflash);
++      unregister_blkdev(ps3nflash_major, DEVICE_NAME);
++}
++
++module_init(ps3nflash_init);
++module_exit(ps3nflash_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("PS3 NOR Flash Storage Driver");
++MODULE_AUTHOR("Sony Corporation");
++MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_NOR_FLASH);
diff --git a/0035-ps3-partition.patch b/0035-ps3-partition.patch
new file mode 100644 (file)
index 0000000..a92764a
--- /dev/null
@@ -0,0 +1,151 @@
+--- a/block/partitions/Kconfig 2013-10-01 14:29:39.166124545 +0200
++++ b/block/partitions/Kconfig 2013-10-01 14:30:10.646126376 +0200
+@@ -261,6 +261,12 @@
+         sysv68).
+         Otherwise, say N.
++config PS3_PARTITION
++      bool "PS3 Partition support"
++      depends on PARTITION_ADVANCED
++      help
++        Say Y here if you would like to use PS3 hard disks under Linux.
++
+ config CMDLINE_PARTITION
+       bool "Command line partition support" if PARTITION_ADVANCED
+       select CMDLINE_PARSER
+--- a/block/partitions/Makefile        2012-09-06 18:41:11.858905657 +0200
++++ b/block/partitions/Makefile        2012-09-06 18:56:12.622291395 +0200
+@@ -18,3 +18,4 @@
+ obj-$(CONFIG_EFI_PARTITION) += efi.o
+ obj-$(CONFIG_KARMA_PARTITION) += karma.o
+ obj-$(CONFIG_SYSV68_PARTITION) += sysv68.o
++obj-$(CONFIG_PS3_PARTITION) += ps3.o
+--- a/block/partitions/check.c 2013-10-01 14:33:08.996136753 +0200
++++ b/block/partitions/check.c 2013-10-01 14:32:17.909467114 +0200
+@@ -34,6 +34,7 @@
+ #include "efi.h"
+ #include "karma.h"
+ #include "sysv68.h"
++#include "ps3.h"
+ #include "cmdline.h"
+ int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
+@@ -108,6 +109,9 @@
+ #ifdef CONFIG_SYSV68_PARTITION
+       sysv68_partition,
+ #endif
++#ifdef CONFIG_PS3_PARTITION
++      ps3_partition,
++#endif
+       NULL
+ };
+--- /dev/null  2012-09-06 18:32:42.202209340 +0200
++++ b/block/partitions/ps3.h   2012-09-06 18:41:52.025574661 +0200
+@@ -0,0 +1,5 @@
++/*
++ *  fs/partitions/ps3.h
++ */
++
++int ps3_partition(struct parsed_partitions *state);
+--- /dev/null  2012-09-06 18:32:42.202209340 +0200
++++ b/block/partitions/ps3.c   2012-09-06 20:56:28.612711200 +0200
+@@ -0,0 +1,98 @@
++/*
++ *  fs/partitions/ps3.c
++ *
++ *  Copyright (C) 2012 glevand <[email protected]>
++ */
++
++#include "check.h"
++#include "ps3.h"
++
++#define SECTOR_SIZE           512
++#define MAX_ACL_ENTRIES               8
++#define MAX_PARTITIONS                8
++
++#define MAGIC1                        0x0FACE0FFULL
++#define MAGIC2                        0xDEADFACEULL
++
++struct p_acl_entry {
++      __be64 laid;
++      __be64 rights;
++};
++
++struct d_partition {
++      __be64 p_start;
++      __be64 p_size;
++      struct p_acl_entry p_acl[MAX_ACL_ENTRIES];
++};
++
++struct disklabel {
++      u8 d_res1[16];
++      __be64 d_magic1;
++      __be64 d_magic2;
++      __be64 d_res2;
++      __be64 d_res3;
++      struct d_partition d_partitions[MAX_PARTITIONS];
++      u8 d_pad[0x600 - MAX_PARTITIONS * sizeof(struct d_partition)- 0x30];
++};
++
++static bool ps3_read_disklabel(struct parsed_partitions *state, struct disklabel *label)
++{
++      Sector sect;
++      unsigned char *data;
++      int i;
++
++      for (i = 0; i < sizeof(struct disklabel) / SECTOR_SIZE; i++) {
++              data = read_part_sector(state, i, &sect);
++              if (!data)
++                      return (false);
++
++              memcpy((unsigned char *) label + i * SECTOR_SIZE, data, SECTOR_SIZE);
++
++              put_dev_sector(sect);
++      }
++
++      return (true);
++}
++
++int ps3_partition(struct parsed_partitions *state)
++{
++      struct disklabel *label = NULL;
++      int slot = 1;
++      int result = -1;
++      int i;
++
++      label = kmalloc(sizeof(struct disklabel), GFP_KERNEL);
++      if (!label)
++              goto out;
++
++      if (!ps3_read_disklabel(state, label))
++              goto out;
++
++      result = 0;
++
++      if ((be64_to_cpu(label->d_magic1) != MAGIC1) ||
++          (be64_to_cpu(label->d_magic2) != MAGIC2))
++              goto out;
++
++      for (i = 0; i < MAX_PARTITIONS; i++) {
++              if (label->d_partitions[i].p_start && label->d_partitions[i].p_size) {
++                      put_partition(state, slot,
++                              be64_to_cpu(label->d_partitions[i].p_start),
++                              be64_to_cpu(label->d_partitions[i].p_size));
++                      slot++;
++              }
++      }
++
++      strlcat(state->pp_buf, "\n", PAGE_SIZE);
++
++      kfree(label);
++
++      return (1);
++
++out:
++
++      if (label)
++              kfree(label);
++
++      return (result);
++}
diff --git a/0040-ps3sysmgr-lpar-reboot.patch b/0040-ps3sysmgr-lpar-reboot.patch
new file mode 100644 (file)
index 0000000..c65ab5e
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/drivers/ps3/ps3-sys-manager.c    2012-01-27 19:35:54.017195402 +0100
++++ b/drivers/ps3/ps3-sys-manager.c    2012-01-27 19:36:18.990903099 +0100
+@@ -656,7 +656,7 @@
+       ps3_vuart_cancel_async(dev);
+       ps3_sys_manager_send_attr(dev, 0);
+-      ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT,
++      ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT,
+               user_wake_sources);
+       ps3_sys_manager_fin(dev);
diff --git a/0050-ps3sysmgr-char-device.patch b/0050-ps3sysmgr-char-device.patch
new file mode 100644 (file)
index 0000000..ca5430d
--- /dev/null
@@ -0,0 +1,174 @@
+--- a/drivers/ps3/ps3-sys-manager.c    2012-01-26 20:35:02.242768746 +0100
++++ b/drivers/ps3/ps3-sys-manager.c    2012-01-26 20:38:07.325547265 +0100
+@@ -3,6 +3,8 @@
+  *
+  *  Copyright (C) 2007 Sony Computer Entertainment Inc.
+  *  Copyright 2007 Sony Corp.
++ *  Copyright (C) 2011 graf_chokolo <[email protected]>.
++ *  Copyright (C) 2011, 2012 glevand <[email protected]>.
+  *
+  *  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
+@@ -22,7 +24,12 @@
+ #include <linux/module.h>
+ #include <linux/workqueue.h>
+ #include <linux/reboot.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <asm/atomic.h>
+ #include <asm/firmware.h>
+ #include <asm/lv1call.h>
+ #include <asm/ps3.h>
+@@ -43,6 +50,14 @@
+  * specific payload.
+  */
++#define DEVICE_NAME           "ps3sysmngr"
++
++static struct ps3sm {
++      struct ps3_system_bus_device *dev;
++      struct miscdevice misc;
++      atomic_t misc_in_use;
++} *ps3sm;
++
+ /**
+  * struct ps3_sys_manager_header - System manager message header.
+  * @version: Header version, currently 1.
+@@ -706,6 +721,86 @@
+       ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
+ }
++static int ps3_sys_manager_misc_open(struct inode *inode, struct file *file)
++{
++      if (atomic_inc_return(&ps3sm->misc_in_use) == 1)
++              ps3_vuart_cancel_async(ps3sm->dev);
++
++      return 0;
++}
++
++static int ps3_sys_manager_misc_release(struct inode *inode, struct file *file)
++{
++      if (atomic_dec_and_test(&ps3sm->misc_in_use))
++              ps3_vuart_read_async(ps3sm->dev, PS3_SM_RX_MSG_LEN_MIN);
++
++      return 0;
++}
++
++static ssize_t ps3_sys_manager_misc_read(struct file *file, char __user *usrbuf,
++                                       size_t count, loff_t *pos)
++{
++      char *buf;
++      int result;
++
++      buf = kmalloc(count, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      result = ps3_vuart_read(ps3sm->dev, buf, count);
++      if (result)
++              goto out;
++
++      if (copy_to_user(usrbuf, buf, count)) {
++              result = -EFAULT;
++              goto out;
++      }
++
++      result = count;
++
++out:
++
++      kfree(buf);
++
++      return result;
++}
++
++static ssize_t ps3_sys_manager_misc_write(struct file *file, const char __user *usrbuf,
++                                        size_t count, loff_t *pos)
++{
++      char *buf;
++      int result;
++
++      buf = kmalloc(count, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      if (copy_from_user(buf, usrbuf, count)) {
++              result = -EFAULT;
++              goto out;
++      }
++
++      result = ps3_vuart_write(ps3sm->dev, buf, count);
++      if (result)
++              goto out;
++
++      result = count;
++
++out:
++
++      kfree(buf);
++
++      return result;
++}
++
++static const struct file_operations ps3_sys_manager_misc_fops = {
++      .owner          = THIS_MODULE,
++      .open           = ps3_sys_manager_misc_open,
++      .release        = ps3_sys_manager_misc_release,
++      .read           = ps3_sys_manager_misc_read,
++      .write          = ps3_sys_manager_misc_write,
++};
++
+ static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
+ {
+       int result;
+@@ -727,12 +822,47 @@
+       result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
+       BUG_ON(result);
++      ps3sm = kzalloc(sizeof(*ps3sm), GFP_KERNEL);
++      if (!ps3sm)
++              goto skip_misc;
++
++      ps3sm->dev = dev;
++
++      ps3sm->misc.parent = &dev->core;
++      ps3sm->misc.minor = MISC_DYNAMIC_MINOR,
++      ps3sm->misc.name = DEVICE_NAME,
++      ps3sm->misc.fops = &ps3_sys_manager_misc_fops,
++      atomic_set(&ps3sm->misc_in_use, 0);
++
++      result = misc_register(&ps3sm->misc);
++      if (result) {
++              dev_err(&dev->core, "%s:%u: misc_register failed %d\n",
++                                  __func__, __LINE__, result);
++              kfree(ps3sm);
++              ps3sm = NULL;
++              goto skip_misc;
++      }
++
++      dev_info(&dev->core, "%s:%u: registered misc device %d\n",
++                           __func__, __LINE__, ps3sm->misc.minor);
++
++skip_misc:
++
++      result = 0;
++
+       return result;
+ }
+ static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev)
+ {
+       dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
++
++      if (ps3sm) {
++              misc_deregister(&ps3sm->misc);
++              kfree(ps3sm);
++              ps3sm = NULL;
++      }
++
+       return 0;
+ }
diff --git a/0060-ps3avmgr-char-device.patch b/0060-ps3avmgr-char-device.patch
new file mode 100644 (file)
index 0000000..4c4ec20
--- /dev/null
@@ -0,0 +1,143 @@
+--- a/drivers/ps3/ps3av.c      2013-03-07 22:13:51.309374686 +0100
++++ b/drivers/ps3/ps3av.c      2013-03-07 22:14:15.449376092 +0100
+@@ -25,6 +25,8 @@
+ #include <linux/ioctl.h>
+ #include <linux/fb.h>
+ #include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/miscdevice.h>
+ #include <asm/firmware.h>
+ #include <asm/ps3av.h>
+@@ -35,6 +37,8 @@
+ #define BUFSIZE          4096 /* vuart buf size */
+ #define PS3AV_BUF_SIZE   512  /* max packet size */
++#define DEVICE_NAME   "ps3avmngr"
++
+ static int safe_mode;
+ static int timeout = 5000;    /* in msec ( 5 sec ) */
+@@ -60,6 +64,8 @@
+               struct ps3av_reply_hdr reply_hdr;
+               u8 raw[PS3AV_BUF_SIZE];
+       } recv_buf;
++      struct miscdevice misc;
++      int misc_ok;
+ } *ps3av;
+ /* color space */
+@@ -932,6 +938,68 @@
+ }
+ EXPORT_SYMBOL_GPL(ps3av_audio_mute);
++static ssize_t ps3av_misc_read(struct file *file, char __user *usrbuf,
++                             size_t count, loff_t *pos)
++{
++      char *buf;
++      int result;
++
++      buf = kmalloc(count, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      result = ps3_vuart_read(ps3av->dev, buf, count);
++      if (result)
++              goto out;
++
++      if (copy_to_user(usrbuf, buf, count)) {
++              result = -EFAULT;
++              goto out;
++      }
++
++      result = count;
++
++out:
++
++      kfree(buf);
++
++      return result;
++}
++
++static ssize_t ps3av_misc_write(struct file *file, const char __user *usrbuf,
++                              size_t count, loff_t *pos)
++{
++      char *buf;
++      int result;
++
++      buf = kmalloc(count, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      if (copy_from_user(buf, usrbuf, count)) {
++              result = -EFAULT;
++              goto out;
++      }
++
++      result = ps3_vuart_write(ps3av->dev, buf, count);
++      if (result)
++              goto out;
++
++      result = count;
++
++out:
++
++      kfree(buf);
++
++      return result;
++}
++
++static const struct file_operations ps3av_misc_fops = {
++      .owner          = THIS_MODULE,
++      .read   = ps3av_misc_read,
++      .write  = ps3av_misc_write,
++};
++
+ static int ps3av_probe(struct ps3_system_bus_device *dev)
+ {
+       int res;
+@@ -1003,11 +1071,34 @@
+       ps3av->ps3av_mode = id;
+       mutex_unlock(&ps3av->mutex);
++      ps3av->misc.parent = &dev->core;
++      ps3av->misc.minor = MISC_DYNAMIC_MINOR,
++      ps3av->misc.name = DEVICE_NAME,
++      ps3av->misc.fops = &ps3av_misc_fops,
++
++      res = misc_register(&ps3av->misc);
++      if (res) {
++              dev_err(&dev->core, "%s:%u: misc_register failed %d\n",
++                                  __func__, __LINE__, res);
++              goto skip_misc;
++      }
++
++      ps3av->misc_ok = 1;
++
++      dev_info(&dev->core, "%s:%u: registered misc device %d\n",
++                           __func__, __LINE__, ps3av->misc.minor);
++
++skip_misc:
++
+       dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
+       return 0;
+ fail:
++
++      if (ps3av->misc_ok)
++              misc_deregister(&ps3av->misc);
++
+       kfree(ps3av);
+       ps3av = NULL;
+       return res;
+@@ -1017,6 +1108,9 @@
+ {
+       dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
+       if (ps3av) {
++              if (ps3av->misc_ok)
++                      misc_deregister(&ps3av->misc);
++
+               ps3av_cmd_fin();
+               if (ps3av->wq)
+                       destroy_workqueue(ps3av->wq);
diff --git a/0070-ps3dispmgr.patch b/0070-ps3dispmgr.patch
new file mode 100644 (file)
index 0000000..a74b1a3
--- /dev/null
@@ -0,0 +1,328 @@
+--- a/drivers/ps3/ps3-vuart.c  2012-01-12 20:42:45.000000000 +0100
++++ b/drivers/ps3/ps3-vuart.c  2012-01-26 18:13:18.981200186 +0100
+@@ -39,6 +39,7 @@
+  * vuart - An inter-partition data link service.
+  *  port 0: PS3 AV Settings.
+  *  port 2: PS3 System Manager.
++ *  port 10: PS3 Dispatcher Manager.
+  *
+  * The vuart provides a bi-directional byte stream data link between logical
+  * partitions.  Its primary role is as a communications link between the guest
+@@ -46,7 +47,7 @@
+  * connections other than those listed.
+  */
+-enum {PORT_COUNT = 3,};
++enum {PORT_COUNT = 11,};
+ enum vuart_param {
+       PARAM_TX_TRIGGER = 0,
+@@ -926,7 +927,7 @@
+       vuart_bus_priv.use_count++;
+-      BUG_ON(vuart_bus_priv.use_count > 2);
++      BUG_ON(vuart_bus_priv.use_count > 3);
+       if (vuart_bus_priv.use_count != 1)
+               return 0;
+--- a/arch/powerpc/include/asm/ps3.h   2012-01-26 14:09:41.132438036 +0100
++++ b/arch/powerpc/include/asm/ps3.h   2012-01-26 14:11:27.368200391 +0100
+@@ -327,6 +327,7 @@
+       PS3_MATCH_ID_GPU                = 10,
+       PS3_MATCH_ID_LPM                = 11,
+       PS3_MATCH_ID_STOR_NOR_FLASH     = 12,
++      PS3_MATCH_ID_DISP_MANAGER       = 13,
+ };
+ enum ps3_match_sub_id {
+@@ -347,6 +348,7 @@
+ #define PS3_MODULE_ALIAS_GPU_RAMDISK  "ps3:10:2"
+ #define PS3_MODULE_ALIAS_LPM          "ps3:11:0"
+ #define PS3_MODULE_ALIAS_STOR_NOR_FLASH       "ps3:12:0"
++#define PS3_MODULE_ALIAS_DISP_MANAGER "ps3:13:0"
+ enum ps3_system_bus_device_type {
+       PS3_DEVICE_TYPE_IOC0 = 1,
+--- a/arch/powerpc/platforms/ps3/system-bus.c  2012-01-26 14:09:41.144403930 +0100
++++ b/arch/powerpc/platforms/ps3/system-bus.c  2012-01-26 14:17:05.896702636 +0100
+@@ -183,6 +183,7 @@
+       case PS3_MATCH_ID_AV_SETTINGS:
+       case PS3_MATCH_ID_SYSTEM_MANAGER:
++      case PS3_MATCH_ID_DISP_MANAGER:
+               pr_debug("%s:%d: unsupported match_id: %u\n", __func__,
+                       __LINE__, dev->match_id);
+               pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__,
+@@ -222,6 +223,7 @@
+       case PS3_MATCH_ID_AV_SETTINGS:
+       case PS3_MATCH_ID_SYSTEM_MANAGER:
++      case PS3_MATCH_ID_DISP_MANAGER:
+               pr_debug("%s:%d: unsupported match_id: %u\n", __func__,
+                       __LINE__, dev->match_id);
+               pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__,
+--- a/arch/powerpc/platforms/ps3/device-init.c 2012-01-26 14:09:41.144403930 +0100
++++ b/arch/powerpc/platforms/ps3/device-init.c 2012-01-26 14:25:10.440202587 +0100
+@@ -426,6 +426,7 @@
+ {
+       int result;
+       unsigned int port_number;
++      u64 lpar_id, laid, junk;
+       pr_debug(" -> %s:%d\n", __func__, __LINE__);
+@@ -444,6 +445,27 @@
+               port_number);
+       WARN_ON(result);
++      result = lv1_get_logical_partition_id(&lpar_id);
++      if (result)
++              goto out;
++
++      result = lv1_read_repository_node(1, 0x0000000073730000ul /* ss */,
++                                           0x6c61696400000000ul /* laid */,
++                                           lpar_id,
++                                           0, &laid, &junk);
++      if (result)
++              goto out;
++
++      if (laid == 0x1070000002000001ul) {
++              port_number = 10;
++              result = ps3_setup_vuart_device(PS3_MATCH_ID_DISP_MANAGER,
++                      port_number);
++      }
++
++out:
++
++      WARN_ON(result);
++
+       pr_debug(" <- %s:%d\n", __func__, __LINE__);
+       return result;
+ }
+--- a/drivers/ps3/Makefile     2012-01-12 20:42:45.000000000 +0100
++++ b/drivers/ps3/Makefile     2012-01-26 17:40:36.400861141 +0100
+@@ -5,3 +5,4 @@
+ obj-$(CONFIG_PS3_SYS_MANAGER) += ps3-sys-manager.o
+ obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o
+ obj-$(CONFIG_PS3_LPM) += ps3-lpm.o
++obj-$(CONFIG_PS3_DISP_MANAGER) += ps3-disp-manager.o
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-01-26 17:37:55.331589081 +0100
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-01-26 17:42:48.049593001 +0100
+@@ -88,6 +88,17 @@
+         This support is required for system control.  In
+         general, all users will say Y or M.
++config PS3_DISP_MANAGER
++      depends on PPC_PS3
++      tristate "PS3 Dispatcher Manager driver" if PS3_ADVANCED
++      select PS3_VUART
++      default y
++      help
++        Include support for the PS3 Dispatcher Manager.
++
++        This support is required to access the PS3 SS services.
++        In general, all users will say Y or M.
++
+ config PS3_STORAGE
+       depends on PPC_PS3
+       tristate
+--- /dev/null  2011-11-07 10:16:29.227396409 +0100
++++ b/drivers/ps3/ps3-disp-manager.c   2012-01-26 15:08:35.100453068 +0100
+@@ -0,0 +1,196 @@
++/*
++ *  PS3 Dispatcher Manager.
++ *
++ *  Copyright (C) 2011 graf_chokolo <[email protected]>.
++ *  Copyright (C) 2011, 2012 glevand <[email protected]>.
++ *
++ *  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; version 2 of the License.
++ *
++ *  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/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++
++#include <asm/firmware.h>
++#include <asm/lv1call.h>
++#include <asm/ps3.h>
++
++#include "vuart.h"
++
++#define DEVICE_NAME           "ps3dispmngr"
++
++static struct ps3dm {
++      struct ps3_system_bus_device *dev;
++      struct miscdevice misc;
++} *ps3dm;
++
++static ssize_t ps3_disp_manager_read(struct file *file, char __user *usrbuf,
++                                   size_t count, loff_t *pos)
++{
++      char *buf;
++      int result;
++
++      buf = kmalloc(count, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      result = ps3_vuart_read(ps3dm->dev, buf, count);
++      if (result)
++              goto out;
++
++      if (copy_to_user(usrbuf, buf, count)) {
++              result = -EFAULT;
++              goto out;
++      }
++
++      result = count;
++
++out:
++
++      kfree(buf);
++
++      return result;
++}
++
++static ssize_t ps3_disp_manager_write(struct file *file, const char __user *usrbuf,
++                                    size_t count, loff_t *pos)
++{
++      char *buf;
++      int result;
++
++      buf = kmalloc(count, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      if (copy_from_user(buf, usrbuf, count)) {
++              result = -EFAULT;
++              goto out;
++      }
++
++      result = ps3_vuart_write(ps3dm->dev, buf, count);
++      if (result)
++              goto out;
++
++      result = count;
++
++out:
++
++      kfree(buf);
++
++      return result;
++}
++
++static const struct file_operations ps3_disp_manager_fops = {
++      .owner  = THIS_MODULE,
++      .read   = ps3_disp_manager_read,
++      .write  = ps3_disp_manager_write,
++};
++
++static int ps3_disp_manager_probe(struct ps3_system_bus_device *dev)
++{
++      int result;
++
++      dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
++
++      if (ps3dm) {
++              dev_err(&dev->core, "Only one Dispatcher Manager is supported\n");
++              return -EBUSY;
++      }
++
++      ps3dm = kzalloc(sizeof(*ps3dm), GFP_KERNEL);
++      if (!ps3dm)
++              return -ENOMEM;
++
++      ps3dm->dev = dev;
++
++      ps3dm->misc.parent = &dev->core;
++      ps3dm->misc.minor = MISC_DYNAMIC_MINOR,
++      ps3dm->misc.name = DEVICE_NAME,
++      ps3dm->misc.fops = &ps3_disp_manager_fops,
++
++      result = misc_register(&ps3dm->misc);
++      if (result) {
++              dev_err(&dev->core, "%s:%u: misc_register failed %d\n",
++                      __func__, __LINE__, result);
++              goto fail;
++      }
++
++      dev_info(&dev->core, "%s:%u: registered misc device %d\n",
++                           __func__, __LINE__, ps3dm->misc.minor);
++
++      dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
++
++      return 0;
++
++fail:
++
++      kfree(ps3dm);
++      ps3dm = NULL;
++
++      return result;
++}
++
++static int ps3_disp_manager_remove(struct ps3_system_bus_device *dev)
++{
++      dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
++
++      if (ps3dm) {
++              misc_deregister(&ps3dm->misc);
++              kfree(ps3dm);
++              ps3dm = NULL;
++      }
++
++      return 0;
++}
++
++static void ps3_disp_manager_shutdown(struct ps3_system_bus_device *dev)
++{
++      dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
++}
++
++static struct ps3_vuart_port_driver ps3_disp_manager = {
++      .core.match_id = PS3_MATCH_ID_DISP_MANAGER,
++      .core.core.name = "ps3_disp_manager",
++      .probe = ps3_disp_manager_probe,
++      .remove = ps3_disp_manager_remove,
++      .shutdown = ps3_disp_manager_shutdown,
++};
++
++static int __init ps3_disp_manager_init(void)
++{
++      if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
++              return -ENODEV;
++
++      return ps3_vuart_port_driver_register(&ps3_disp_manager);
++}
++
++static void __exit ps3_disp_manager_exit(void)
++{
++      pr_debug(" -> %s:%d\n", __func__, __LINE__);
++
++      ps3_vuart_port_driver_unregister(&ps3_disp_manager);
++
++      pr_debug(" <- %s:%d\n", __func__, __LINE__);
++}
++
++module_init(ps3_disp_manager_init);
++module_exit(ps3_disp_manager_exit);
++
++MODULE_AUTHOR("glevand");
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("PS3 Dispatcher Manager");
++MODULE_ALIAS(PS3_MODULE_ALIAS_DISP_MANAGER);
diff --git a/0080-ps3rom-vendor-specific-command.patch b/0080-ps3rom-vendor-specific-command.patch
new file mode 100644 (file)
index 0000000..685f6c6
--- /dev/null
@@ -0,0 +1,63 @@
+--- a/drivers/scsi/ps3rom.c    2012-02-13 18:45:43.735156562 +0100
++++ b/drivers/scsi/ps3rom.c    2012-02-13 18:45:54.835348606 +0100
+@@ -40,6 +40,8 @@
+ #define PS3ROM_MAX_SECTORS            (BOUNCE_SIZE >> 9)
++#define PS3ROM_VENDOR_SPECIFIC_OPCODE 0xfd
++
+ struct ps3rom_private {
+       struct ps3_storage_device *dev;
+@@ -165,6 +167,40 @@
+       return 0;
+ }
++static int ps3rom_vendor_specific_request(struct ps3_storage_device *dev,
++                                        struct scsi_cmnd *cmd)
++{
++      unsigned char opcode = cmd->cmnd[1];
++      int res;
++
++      dev_dbg(&dev->sbd.core, "%s:%u: send vendor-specific command 0x%02x\n", __func__,
++              __LINE__, opcode);
++
++      if (cmd->sc_data_direction == DMA_TO_DEVICE)
++              scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size);
++
++      res = lv1_storage_send_device_command(dev->sbd.dev_id,
++                                            opcode,
++                                            dev->bounce_lpar, scsi_bufflen(cmd),
++                                            dev->bounce_lpar, dev->bounce_size,
++                                            &dev->tag);
++      if (res == LV1_DENIED_BY_POLICY) {
++              dev_dbg(&dev->sbd.core,
++                      "%s:%u: vendor-specific command 0x%02x denied by policy\n",
++                      __func__, __LINE__, opcode);
++              return DID_ERROR << 16;
++      }
++
++      if (res) {
++              dev_err(&dev->sbd.core,
++                      "%s:%u: vendor-specific command 0x%02x failed %d\n", __func__,
++                      __LINE__, opcode, res);
++              return DID_ERROR << 16;
++      }
++
++      return 0;
++}
++
+ static inline unsigned int srb10_lba(const struct scsi_cmnd *cmd)
+ {
+       return cmd->cmnd[2] << 24 | cmd->cmnd[3] << 16 | cmd->cmnd[4] << 8 |
+@@ -254,6 +290,10 @@
+                                          srb10_len(cmd));
+               break;
++      case PS3ROM_VENDOR_SPECIFIC_OPCODE:
++              res = ps3rom_vendor_specific_request(dev, cmd);
++              break;
++
+       default:
+               res = ps3rom_atapi_request(dev, cmd);
+               break;
diff --git a/0090-spu-enum-shared-param.patch b/0090-spu-enum-shared-param.patch
new file mode 100644 (file)
index 0000000..539623b
--- /dev/null
@@ -0,0 +1,48 @@
+--- a/arch/powerpc/include/asm/spu.h   2012-03-01 01:32:49.000000000 +0100
++++ b/arch/powerpc/include/asm/spu.h   2012-03-05 17:49:44.083370504 +0100
+@@ -198,6 +198,8 @@
+ extern struct cbe_spu_info cbe_spu_info[];
++extern int spu_enum_shared(void);
++
+ void spu_init_channels(struct spu *spu);
+ void spu_irq_setaffinity(struct spu *spu, int cpu);
+--- a/arch/powerpc/platforms/cell/spu_base.c   2012-03-01 01:32:49.000000000 +0100
++++ b/arch/powerpc/platforms/cell/spu_base.c   2012-03-05 17:52:47.659547098 +0100
+@@ -40,6 +40,10 @@
+ #include <asm/prom.h>
+ #include <asm/kexec.h>
++static int enum_shared = 0;
++module_param(enum_shared, int, S_IRUGO);
++MODULE_PARM_DESC(enum_shared, "Enumerate shared SPUs");
++
+ const struct spu_management_ops *spu_management_ops;
+ EXPORT_SYMBOL_GPL(spu_management_ops);
+@@ -484,6 +488,12 @@
+               free_irq(spu->irqs[2], spu);
+ }
++int spu_enum_shared(void)
++{
++      return (enum_shared);
++}
++EXPORT_SYMBOL_GPL(spu_enum_shared);
++
+ void spu_init_channels(struct spu *spu)
+ {
+       static const struct {
+--- a/arch/powerpc/platforms/ps3/spu.c 2012-03-01 01:32:49.000000000 +0100
++++ b/arch/powerpc/platforms/ps3/spu.c 2012-03-05 17:54:01.800664787 +0100
+@@ -421,7 +421,7 @@
+               if (result)
+                       break;
+-              if (resource_type == PS3_SPU_RESOURCE_TYPE_EXCLUSIVE) {
++              if (resource_type == PS3_SPU_RESOURCE_TYPE_EXCLUSIVE || spu_enum_shared()) {
+                       result = fn((void*)(unsigned long)resource_id);
+                       if (result)
diff --git a/0100-lv1call-repo-node-lparid-param.patch b/0100-lv1call-repo-node-lparid-param.patch
new file mode 100644 (file)
index 0000000..b27e20c
--- /dev/null
@@ -0,0 +1,16 @@
+--- a/arch/powerpc/include/asm/lv1call.h       2012-01-26 01:39:32.000000000 +0100
++++ b/arch/powerpc/include/asm/lv1call.h       2012-01-31 20:52:04.932882025 +0100
+@@ -263,10 +263,10 @@
+ LV1_CALL(configure_execution_time_variable,             1, 0,  77 )
+ LV1_CALL(get_spe_irq_outlet,                            2, 1,  78 )
+ LV1_CALL(set_spe_privilege_state_area_1_register,       3, 0,  79 )
+-LV1_CALL(create_repository_node,                        6, 0,  90 )
++LV1_CALL(create_repository_node,                        7, 0,  90 )
+ LV1_CALL(read_repository_node,                          5, 2,  91 )
+-LV1_CALL(write_repository_node,                         6, 0,  92 )
+-LV1_CALL(delete_repository_node,                        4, 0,  93 )
++LV1_CALL(write_repository_node,                         7, 0,  92 )
++LV1_CALL(delete_repository_node,                        5, 0,  93 )
+ LV1_CALL(read_htab_entries,                             2, 5,  95 )
+ LV1_CALL(set_dabr,                                      2, 0,  96 )
+ LV1_CALL(get_total_execution_time,                      2, 1, 103 )
diff --git a/0110-lv1call-add-hvcalls-114-115.patch b/0110-lv1call-add-hvcalls-114-115.patch
new file mode 100644 (file)
index 0000000..3b8935b
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/arch/powerpc/include/asm/lv1call.h       2012-02-03 21:39:51.000000000 +0100
++++ b/arch/powerpc/include/asm/lv1call.h       2012-02-05 17:34:26.973058855 +0100
+@@ -270,6 +270,8 @@
+ LV1_CALL(read_htab_entries,                             2, 5,  95 )
+ LV1_CALL(set_dabr,                                      2, 0,  96 )
+ LV1_CALL(get_total_execution_time,                      2, 1, 103 )
++LV1_CALL(undocumented_function_114,                     3, 1, 114 )
++LV1_CALL(undocumented_function_115,                     1, 0, 115 )
+ LV1_CALL(allocate_io_segment,                           3, 1, 116 )
+ LV1_CALL(release_io_segment,                            2, 0, 117 )
+ LV1_CALL(construct_io_irq_outlet,                       1, 1, 120 )
diff --git a/0120-lv1call-add-storage-region-hvcalls.patch b/0120-lv1call-add-storage-region-hvcalls.patch
new file mode 100644 (file)
index 0000000..46b662c
--- /dev/null
@@ -0,0 +1,13 @@
+--- a/arch/powerpc/include/asm/lv1call.h       2012-02-07 18:22:20.218981765 +0100
++++ b/arch/powerpc/include/asm/lv1call.h       2012-02-08 19:44:40.636013978 +0100
+@@ -327,6 +327,10 @@
+ LV1_CALL(storage_write,                                 6, 1, 246 )
+ LV1_CALL(storage_send_device_command,                   6, 1, 248 )
+ LV1_CALL(storage_get_async_status,                      1, 2, 249 )
++LV1_CALL(storage_create_region,                         5, 2, 250 )
++LV1_CALL(storage_delete_region,                         2, 1, 251 )
++LV1_CALL(storage_set_region_acl,                        4, 1, 252 )
++LV1_CALL(storage_get_region_acl,                        3, 2, 253 )
+ LV1_CALL(storage_check_async_status,                    2, 1, 254 )
+ LV1_CALL(panic,                                         1, 0, 255 )
+ LV1_CALL(construct_lpm,                                 6, 3, 140 )
diff --git a/0130-ps3physmem.patch b/0130-ps3physmem.patch
new file mode 100644 (file)
index 0000000..d3b25da
--- /dev/null
@@ -0,0 +1,211 @@
+--- a/drivers/char/Makefile    2012-02-03 21:39:51.000000000 +0100
++++ b/drivers/char/Makefile    2012-02-05 17:51:57.339262786 +0100
+@@ -65,3 +65,5 @@
+ js-rtc-y = rtc.o
+ obj-$(CONFIG_TILE_SROM)               += tile-srom.o
++
++obj-$(CONFIG_PS3_PHYSMEM)     += ps3physmem.o
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-02-04 21:13:42.539806178 +0100
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-02-05 17:54:38.698430717 +0100
+@@ -179,6 +179,12 @@
+         profiling support of the Cell processor with programs like
+         oprofile and perfmon2, then say Y or M, otherwise say N.
++config PS3_PHYSMEM
++      tristate "PS3 Physical Memory Driver"
++      depends on PPC_PS3
++      help
++        This driver allows you direct access to the PS3 physical memory.
++
+ config PS3GELIC_UDBG
+       bool "PS3 udbg output via UDP broadcasts on Ethernet"
+       depends on PPC_PS3
+--- /dev/null  2012-02-05 10:06:29.087361680 +0100
++++ b/drivers/char/ps3physmem.c        2012-02-05 17:50:30.897909862 +0100
+@@ -0,0 +1,185 @@
++/*
++ * PS3 Physical Memory Driver
++ *
++ * Copyright (C) 2011 graf_chokolo <[email protected]>
++ * Copyright (C) 2011, 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mm.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++#include <linux/miscdevice.h>
++
++#include <asm/io.h>
++#include <asm/ps3.h>
++#include <asm/lv1call.h>
++
++static unsigned long mem_start = 0;
++module_param(mem_start, ulong, 0);
++
++static unsigned long mem_size = 256 * 1024 * 1024;
++module_param(mem_size, ulong, 0);
++
++static unsigned long mem_pagesize = 24;       /* 16MB */
++module_param(mem_pagesize, ulong, 0);
++
++static u64 ps3physmem_lpar;
++static char *ps3physmem_virt;
++
++static ssize_t ps3physmem_read(struct file *file, char __user *usrbuf,
++    size_t count, loff_t *pos)
++{
++      if (*pos + count > mem_size)
++              count = mem_size - *pos;
++
++      if (!count)
++              return (0);
++
++      if (copy_to_user(usrbuf, ps3physmem_virt + *pos, count))
++              return (-EFAULT);
++
++      *pos += count;
++
++      return (count);
++}
++
++static ssize_t ps3physmem_write(struct file *file, const char __user *usrbuf,
++    size_t count, loff_t *pos)
++{
++      if (*pos + count > mem_size)
++              count = mem_size - *pos;
++
++      if (!count)
++              return (0);
++
++      if (copy_from_user(ps3physmem_virt + *pos, usrbuf, count))
++              return (-EFAULT);
++
++      *pos += count;
++
++      return (count);
++}
++
++static void ps3physmem_vma_open(struct vm_area_struct *vma)
++{
++}
++
++static void ps3physmem_vma_close(struct vm_area_struct *vma)
++{
++}
++
++static struct vm_operations_struct ps3physmem_vm_ops = {
++      .open = ps3physmem_vma_open,
++      .close = ps3physmem_vma_close,
++};
++
++static int ps3physmem_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      unsigned long size, pfn;
++      int res;
++
++      size = vma->vm_end - vma->vm_start;
++
++      if (((vma->vm_pgoff << PAGE_SHIFT) + size) > mem_size)
++              return (-EINVAL);
++
++      vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++      pfn = ((unsigned long) ps3physmem_virt >> PAGE_SHIFT) + vma->vm_pgoff;
++
++      res = io_remap_pfn_range(vma, vma->vm_start, pfn, size,
++          vma->vm_page_prot);
++      if (res)
++              return (res);
++
++      vma->vm_ops = &ps3physmem_vm_ops;
++
++      ps3physmem_vma_open(vma);
++
++      return (0);
++}
++
++static const struct file_operations ps3physmem_fops = {
++      .owner = THIS_MODULE,
++      .read = ps3physmem_read,
++      .write = ps3physmem_write,
++      .mmap = ps3physmem_mmap,
++};
++
++static struct miscdevice ps3physmem_misc = {
++      .minor  = MISC_DYNAMIC_MINOR,
++      .name   = "ps3physmem",
++      .fops   = &ps3physmem_fops,
++};
++
++static int __init ps3physmem_init(void)
++{
++      int res;
++
++      res = lv1_undocumented_function_114(mem_start,
++          mem_pagesize, mem_size, &ps3physmem_lpar);
++      if (res) {
++              pr_info("%s:%u: lv1_undocumented_function_114 failed %d\n",
++                  __func__, __LINE__, res);
++              return (-ENXIO);
++      }
++
++      ps3physmem_virt = ioremap_nocache(ps3physmem_lpar, mem_size);
++      if (!ps3physmem_virt) {
++              pr_info("%s:%u: ioremap_nocache failed\n", __func__, __LINE__);
++              goto destroy_lpar;
++      }
++
++      res = misc_register(&ps3physmem_misc);
++      if (res) {
++              pr_info("%s:%u: misc_register failed %d\n",
++                  __func__, __LINE__, res);
++              goto unmap_lpar;
++      }
++
++      return (0);
++
++unmap_lpar:
++
++      iounmap((void *) ps3physmem_virt);
++
++destroy_lpar:
++
++      lv1_undocumented_function_115(ps3physmem_lpar);
++
++      return (res);
++}
++
++static void __exit ps3physmem_exit(void)
++{
++      misc_deregister(&ps3physmem_misc);
++
++      iounmap((void *) ps3physmem_virt);
++
++      lv1_undocumented_function_115(ps3physmem_lpar);
++}
++
++module_init(ps3physmem_init);
++module_exit(ps3physmem_exit);
++
++MODULE_AUTHOR("glevand");
++MODULE_DESCRIPTION("PS3 Physical Memory Driver");
++MODULE_LICENSE("GPL");
diff --git a/0140-ps3strgmngr.patch b/0140-ps3strgmngr.patch
new file mode 100644 (file)
index 0000000..057ccd2
--- /dev/null
@@ -0,0 +1,565 @@
+--- a/drivers/char/Makefile    2012-02-08 19:49:18.736981163 +0100
++++ b/drivers/char/Makefile    2012-02-10 18:43:23.927197754 +0100
+@@ -67,3 +67,4 @@
+ obj-$(CONFIG_TILE_SROM)               += tile-srom.o
+ obj-$(CONFIG_PS3_PHYSMEM)     += ps3physmem.o
++obj-$(CONFIG_PS3_STRGMNGR)    += ps3strgmngr.o
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-02-08 19:49:18.736981163 +0100
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-02-10 18:46:54.907113221 +0100
+@@ -185,6 +185,13 @@
+       help
+         This driver allows you direct access to the PS3 physical memory.
++config PS3_STRGMNGR
++      tristate "PS3 Storage Manager Driver"
++      depends on PPC_PS3
++      help
++        This driver allows you to create/delete/modify regions
++        on PS3 storage devices.
++
+ config PS3GELIC_UDBG
+       bool "PS3 udbg output via UDP broadcasts on Ethernet"
+       depends on PPC_PS3
+--- a/arch/powerpc/include/uapi/asm/Kbuild     2012-12-11 10:00:27.000000000 -0900
++++ b/arch/powerpc/include/uapi/asm/Kbuild     2012-12-11 10:00:46.000000000 -0900
+@@ -42,3 +42,4 @@
+ header-y += types.h
+ header-y += ucontext.h
+ header-y += unistd.h
++header-y += ps3strgmngr.h
+--- /dev/null  2012-02-10 17:03:27.997801619 +0100
++++ b/arch/powerpc/include/asm/ps3strgmngr.h   2012-02-10 18:49:42.806463666 +0100
+@@ -0,0 +1,101 @@
++/*
++ * PS3 Storage Manager Driver
++ *
++ * Copyright (C) 2011 graf_chokolo <[email protected]>
++ * Copyright (C) 2011, 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#ifndef _PS3STRGMNGR_H
++#define _PS3STRGMNGR_H
++
++#include <linux/types.h>
++#include <linux/ioctl.h>
++
++#define PS3STRGMNGR_MAX_ACL_ENTRIES           8
++#define PS3STRGMNGR_MAX_REGIONS                       8
++#define PS3STRGMNGR_MAX_DEVICES                       8
++
++#define PS3STRGMNGR_CTL_GET_DEVICES           _IOR('s', 0, int)
++#define PS3STRGMNGR_CTL_CREATE_REGION         _IOWR('s', 1, int)
++#define PS3STRGMNGR_CTL_DELETE_REGION         _IOW('s', 2, int)
++#define PS3STRGMNGR_CTL_SET_REGION_ACL_ENTRY  _IOW('s', 3, int)
++
++enum ps3strgmngr_devtype {
++      PS3STRGMNGR_DEVTYPE_DISK = 0,
++      PS3STRGMNGR_DEVTYPE_CDROM,
++      PS3STRGMNGR_DEVTYPE_FLASH,
++      PS3STRGMNGR_DEVTYPE_NOR_FLASH,
++};
++
++enum ps3strgmngr_region_access_rights {
++      PS3STRGMNGR_REGION_READ         = (1ull << 0),
++      PS3STRGMNGR_REGION_WRITE        = (1ull << 1),
++};
++
++struct ps3strgmngr_acl_entry {
++      __u64 laid;
++      __u64 access_rights;    /* enum ps3strgmngr_region_access_rights */
++};
++
++struct ps3strgmngr_region {
++      __u64 id;
++      __u64 start_block;
++      __u64 nr_blocks;
++      __u64 nr_acl_entries;
++      struct ps3strgmngr_acl_entry acl_entry[PS3STRGMNGR_MAX_ACL_ENTRIES];
++};
++
++struct ps3strgmngr_device {
++      __u64 type;             /* enum ps3strgmngr_devtype */
++      __u64 id;
++      __u64 block_size;
++      __u64 nr_blocks;
++      __u64 nr_regions;
++      struct ps3strgmngr_region region[PS3STRGMNGR_MAX_REGIONS];
++};
++
++struct ps3strgmngr_ctl_get_devices {
++      /* out */
++      __u64 nr_devices;
++      struct ps3strgmngr_device device[PS3STRGMNGR_MAX_DEVICES];
++};
++
++struct ps3strgmngr_ctl_create_region {
++      /* in */
++      __u64 device_id;
++      __u64 start_block;
++      __u64 nr_blocks;
++      __u64 laid;
++      /* out */
++      __u64 region_id;
++};
++
++struct ps3strgmngr_ctl_delete_region {
++      /* in */
++      __u64 device_id;
++      __u64 region_id;
++};
++
++struct ps3strgmngr_ctl_set_region_acl_entry {
++      /* in */
++      __u64 device_id;
++      __u64 region_id;
++      __u64 laid;
++      __u64 access_rights;
++};
++
++#endif /* _PS3STRGMNGR_H */
+--- /dev/null  2012-02-10 17:03:27.997801619 +0100
++++ b/drivers/char/ps3strgmngr.c       2012-02-10 23:29:41.429492084 +0100
+@@ -0,0 +1,428 @@
++/*
++ * PS3 Storage Manager Driver
++ *
++ * Copyright (C) 2011 graf_chokolo <[email protected]>
++ * Copyright (C) 2011, 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++#include <linux/compat.h>
++#include <linux/miscdevice.h>
++
++#include <asm/ps3.h>
++#include <asm/lv1call.h>
++#include <asm/ps3strgmngr.h>
++
++#define STORAGE_BUS_TYPE              5
++
++#define STORAGE_DEV_TYPE_DISK         0       
++#define STORAGE_DEV_TYPE_CDROM                5       
++#define STORAGE_DEV_TYPE_FLASH                14
++#define STORAGE_DEV_TYPE_NOR_FLASH    254
++
++static int ps3strgmngr_read_acl(u64 dev_id, struct ps3strgmngr_region *rgn)
++{
++      struct ps3strgmngr_acl_entry *acl_entry;
++      unsigned int acl_entry_index;
++      int res;
++
++      rgn->nr_acl_entries = 0;
++      acl_entry = rgn->acl_entry;
++
++      for (acl_entry_index = 0; acl_entry_index < PS3STRGMNGR_MAX_ACL_ENTRIES; acl_entry_index++) {
++              res = lv1_storage_get_region_acl(dev_id, rgn->id, acl_entry_index,
++                  &acl_entry->laid, &acl_entry->access_rights);
++              if (res)
++                      continue;
++
++              rgn->nr_acl_entries++;
++              acl_entry++;
++      }
++
++      return (0);
++}
++
++static int ps3strgmngr_read_region(u64 bus_index, u64 dev_index, u64 dev_id, u64 dev_type,
++    u64 rgn_index, struct ps3strgmngr_region *rgn)
++{
++      u64 flash_ext_flag, junk;
++      int res;
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x726567696f6e0000ul | rgn_index /* region */,
++                                        0x6964000000000000ul /* id */,
++                                        &rgn->id, &junk);
++      if (res)
++              return (-ENXIO);
++
++      if (dev_type == STORAGE_DEV_TYPE_DISK) {
++              res = lv1_read_repository_node(1, 0x0000000073797300ul /* sys */,
++                                                0x666c617368000000ul /* flash */,
++                                                0x6578740000000000ul /* ext */,
++                                                0, &flash_ext_flag, &junk);
++              if (res)
++                      return (-ENXIO);
++
++              if (!(flash_ext_flag & 0x1) && (rgn->id > 0))
++                      rgn->id += 1;
++      }
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x726567696f6e0000ul | rgn_index /* region */,
++                                        0x7374617274000000ul /* start */,
++                                        &rgn->start_block, &junk);
++      if (res)
++              return (-ENXIO);
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x726567696f6e0000ul | rgn_index /* region */,
++                                        0x73697a6500000000ul /* size */,
++                                        &rgn->nr_blocks, &junk);
++      if (res)
++              return (-ENXIO);
++
++      res = ps3strgmngr_read_acl(dev_id, rgn);
++      if (res)
++              return (res);
++
++      return (0);
++}
++
++static int ps3strgmngr_read_device(u64 bus_index, u64 dev_index,
++    struct ps3strgmngr_device *dev)
++{
++      struct ps3strgmngr_region *rgn;
++      u64 type, nr_regions, junk;
++      unsigned int rgn_index;
++      int res;
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x7479706500000000ul /* type */,
++                                        0, &type, &junk);
++      if (res)
++              return (-ENXIO);
++
++      switch (type) {
++      case STORAGE_DEV_TYPE_DISK:
++              dev->type = PS3STRGMNGR_DEVTYPE_DISK;
++      break;
++      case STORAGE_DEV_TYPE_CDROM:
++              dev->type = PS3STRGMNGR_DEVTYPE_CDROM;
++      break;
++      case STORAGE_DEV_TYPE_FLASH:
++              dev->type = PS3STRGMNGR_DEVTYPE_FLASH;
++      break;
++      case STORAGE_DEV_TYPE_NOR_FLASH:
++              dev->type = PS3STRGMNGR_DEVTYPE_NOR_FLASH;
++      break;
++      default:
++              return (-ENOTSUPP);
++      break;
++      }
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x6964000000000000ul /* id */,
++                                        0, &dev->id, &junk);
++      if (res)
++              return (-ENXIO);
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x626c6b5f73697a65ul /* blk_size */,
++                                        0, &dev->block_size, &junk);
++      if (res)
++              return (-ENXIO);
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x6e5f626c6f636b73ul /* n_blocks */,
++                                        0, &dev->nr_blocks, &junk);
++      if (res)
++              return (-ENXIO);
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6465760000000000ul | dev_index /* dev */,
++                                        0x6e5f726567730000ul /* n_regs */,
++                                        0, &nr_regions, &junk);
++      if (res)
++              return (-ENXIO);
++
++      rgn = dev->region;
++
++      for (rgn_index = 0; rgn_index < nr_regions; rgn_index++) {
++              res = ps3strgmngr_read_region(bus_index, dev_index, dev->id, type,
++                  rgn_index, rgn);
++              if (res)
++                      continue;
++
++              dev->nr_regions++;
++              rgn++;
++      }
++
++      return (0);
++}
++
++static int ps3strgmngr_ctl_get_devices(struct ps3strgmngr_ctl_get_devices *get_devices)
++{
++      struct ps3strgmngr_device *dev;
++      unsigned int bus_index, dev_index;
++      u64 bus_type, bus_id, nr_devices, junk;
++      int res;
++
++      get_devices->nr_devices = 0;
++      dev = get_devices->device;
++
++      for (bus_index = 0; bus_index < 10; bus_index++) {
++              res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                                0x7479706500000000ul /* type */,
++                                                0, 0, &bus_type, &junk);
++              if (res)
++                      continue;
++
++              if (bus_type == STORAGE_BUS_TYPE)
++                      break;
++      }
++
++      if (bus_index >= 10)
++              return (-ENXIO);
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6964000000000000ul /* id */,
++                                        0, 0, &bus_id, &junk);
++      if (res)
++              return (-ENXIO);
++
++      res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */,
++                                        0x6e756d5f64657600ul /* num_dev */,
++                                        0, 0, &nr_devices, &junk);
++      if (res)
++              return (-ENXIO);
++
++      for (dev_index = 0; dev_index < nr_devices; dev_index++) {
++              res = ps3strgmngr_read_device(bus_index, dev_index, dev);
++              if (res)
++                      continue;
++
++              get_devices->nr_devices++;
++              dev++;
++      }
++
++      return (0);
++}
++
++static int ps3strgmngr_ctl_create_region(struct ps3strgmngr_ctl_create_region *create_region)
++{
++      u64 tag;
++      int res;
++
++      res = lv1_storage_create_region(create_region->device_id, create_region->start_block,
++          create_region->nr_blocks, 0, create_region->laid, &create_region->region_id, &tag);
++      if (res)
++              return (-ENXIO);
++
++      return (0);
++}
++
++static int ps3strgmngr_ctl_delete_region(struct ps3strgmngr_ctl_delete_region *delete_region)
++{
++      u64 tag;
++      int res;
++
++      res = lv1_storage_delete_region(delete_region->device_id, delete_region->region_id, &tag);
++      if (res)
++              return (-ENXIO);
++
++      return (0);
++}
++
++static int ps3strgmngr_ctl_set_region_acl_entry(struct ps3strgmngr_ctl_set_region_acl_entry *set_region_acl_entry)
++{
++      u64 tag;
++      int res;
++
++      res = lv1_storage_set_region_acl(set_region_acl_entry->device_id, set_region_acl_entry->region_id,
++          set_region_acl_entry->laid, set_region_acl_entry->access_rights,
++          &tag);
++      if (res)
++              return (-ENXIO);
++
++      return (0);
++}
++
++static long ps3strgmngr_ioctl(struct file *file, unsigned int cmd,
++    unsigned long arg)
++{
++      void __user *argp = (void __user *) arg;
++      struct ps3strgmngr_ctl_get_devices *get_devices;
++      struct ps3strgmngr_ctl_create_region *create_region;
++      struct ps3strgmngr_ctl_delete_region *delete_region;
++      struct ps3strgmngr_ctl_set_region_acl_entry *set_region_acl_entry;
++      int res;
++
++      if (is_compat_task())
++              argp = compat_ptr(arg);
++      else
++              argp = (void __user *) arg;
++
++      switch (cmd) {
++      case PS3STRGMNGR_CTL_GET_DEVICES:
++              get_devices = kmalloc(sizeof(*get_devices), GFP_KERNEL);
++              if (!get_devices)
++                      return (-ENOMEM);
++
++              if (copy_from_user(get_devices, argp, sizeof(*get_devices))) {
++                      kfree(get_devices);
++                      return (-EFAULT);
++              }
++
++              res = ps3strgmngr_ctl_get_devices(get_devices);
++              if (res) {
++                      kfree(get_devices);
++                      return (res);
++              }
++
++              if (copy_to_user(argp, get_devices, sizeof(*get_devices))) {
++                      kfree(get_devices);
++                      return (-EFAULT);
++              }
++
++              kfree(get_devices);
++      break;
++      case PS3STRGMNGR_CTL_CREATE_REGION:
++              create_region = kmalloc(sizeof(*create_region), GFP_KERNEL);
++              if (!create_region)
++                      return (-ENOMEM);
++
++              if (copy_from_user(create_region, argp, sizeof(*create_region))) {
++                      kfree(create_region);
++                      return (-EFAULT);
++              }
++
++              res = ps3strgmngr_ctl_create_region(create_region);
++              if (res) {
++                      kfree(create_region);
++                      return (res);
++              }
++
++              if (copy_to_user(argp, create_region, sizeof(*create_region))) {
++                      kfree(create_region);
++                      return (-EFAULT);
++              }
++
++              kfree(create_region);
++      break;
++      case PS3STRGMNGR_CTL_DELETE_REGION:
++              delete_region = kmalloc(sizeof(*delete_region), GFP_KERNEL);
++              if (!delete_region)
++                      return (-ENOMEM);
++
++              if (copy_from_user(delete_region, argp, sizeof(*delete_region))) {
++                      kfree(delete_region);
++                      return (-EFAULT);
++              }
++
++              res = ps3strgmngr_ctl_delete_region(delete_region);
++              if (res) {
++                      kfree(delete_region);
++                      return (res);
++              }
++
++              if (copy_to_user(argp, delete_region, sizeof(*delete_region))) {
++                      kfree(delete_region);
++                      return (-EFAULT);
++              }
++
++              kfree(delete_region);
++      break;
++      case PS3STRGMNGR_CTL_SET_REGION_ACL_ENTRY:
++              set_region_acl_entry = kmalloc(sizeof(*set_region_acl_entry), GFP_KERNEL);
++              if (!set_region_acl_entry)
++                      return (-ENOMEM);
++
++              if (copy_from_user(set_region_acl_entry, argp, sizeof(*set_region_acl_entry))) {
++                      kfree(set_region_acl_entry);
++                      return (-EFAULT);
++              }
++
++              res = ps3strgmngr_ctl_set_region_acl_entry(set_region_acl_entry);
++              if (res) {
++                      kfree(set_region_acl_entry);
++                      return (res);
++              }
++
++              if (copy_to_user(argp, set_region_acl_entry, sizeof(*set_region_acl_entry))) {
++                      kfree(set_region_acl_entry);
++                      return (-EFAULT);
++              }
++
++              kfree(set_region_acl_entry);
++      break;
++      default:
++              return (-ENOIOCTLCMD);
++      break;
++      }
++
++      return (0);
++}
++
++static const struct file_operations ps3strgmngr_fops = {
++      .owner          = THIS_MODULE,
++      .unlocked_ioctl = ps3strgmngr_ioctl,
++      .compat_ioctl   = ps3strgmngr_ioctl,
++};
++
++static struct miscdevice ps3strgmngr_misc = {
++      .minor  = MISC_DYNAMIC_MINOR,
++      .name   = "ps3strgmngr",
++      .fops   = &ps3strgmngr_fops,
++};
++
++static int __init ps3strgmngr_init(void)
++{
++      int res;
++
++      res = misc_register(&ps3strgmngr_misc);
++      if (res) {
++              pr_info("%s:%u: misc_register failed %d\n",
++                  __func__, __LINE__, res);
++              return (res);
++      }
++
++      return (0);
++}
++
++static void __exit ps3strgmngr_exit(void)
++{
++      misc_deregister(&ps3strgmngr_misc);
++}
++
++module_init(ps3strgmngr_init);
++module_exit(ps3strgmngr_exit);
++
++MODULE_AUTHOR("glevand");
++MODULE_DESCRIPTION("PS3 Storage Manager Driver");
++MODULE_LICENSE("GPL");
diff --git a/0150-ps3jupiter.patch b/0150-ps3jupiter.patch
new file mode 100644 (file)
index 0000000..c17c13e
--- /dev/null
@@ -0,0 +1,4872 @@
+--- a/drivers/net/wireless/Kconfig     2013-07-18 03:19:44.349296942 +0200
++++ b/drivers/net/wireless/Kconfig     2013-07-18 03:20:20.909299068 +0200
+@@ -281,5 +281,6 @@
+ source "drivers/net/wireless/zd1211rw/Kconfig"
+ source "drivers/net/wireless/mwifiex/Kconfig"
+ source "drivers/net/wireless/cw1200/Kconfig"
++source "drivers/net/wireless/ps3jupiter/Kconfig"
+ endif # WLAN
+--- a/drivers/net/wireless/Makefile    2013-07-18 03:19:51.919297381 +0200
++++ b/drivers/net/wireless/Makefile    2013-07-18 03:20:40.309300197 +0200
+@@ -59,3 +59,5 @@
+ obj-$(CONFIG_BRCMSMAC)        += brcm80211/
+ obj-$(CONFIG_CW1200)  += cw1200/
++
++obj-$(CONFIG_PS3_JUPITER)     += ps3jupiter/
+--- /dev/null  2012-01-06 10:29:22.673734242 +0100
++++ b/drivers/net/wireless/ps3jupiter/Kconfig  2012-01-06 12:13:47.507142753 +0100
+@@ -0,0 +1,16 @@
++
++config PS3_JUPITER
++      tristate "PS3 Jupiter 802.11bg support"
++      depends on USB
++      ---help---
++          A driver for the PS3 Jupiter
++          802.11bg wireless network adapter.
++
++config PS3_JUPITER_STA
++      tristate "PS3 Jupiter 802.11bg station support"
++      depends on PS3_JUPITER
++      select WIRELESS_EXT
++      select WEXT_PRIV
++      ---help---
++          A station driver for the PS3 Jupiter
++          802.11bg wireless network adapter.
+--- /dev/null  2012-01-06 10:29:22.673734242 +0100
++++ b/drivers/net/wireless/ps3jupiter/Makefile 2012-01-06 12:07:05.647715657 +0100
+@@ -0,0 +1,3 @@
++
++obj-$(CONFIG_PS3_JUPITER) += ps3_jupiter.o
++obj-$(CONFIG_PS3_JUPITER_STA) += ps3_jupiter_sta.o
+--- /dev/null  2012-11-01 00:09:58.397610712 -0800
++++ b/drivers/net/wireless/ps3jupiter/ps3_eurus.h      2012-11-01 03:22:11.000000000 -0800
+@@ -0,0 +1,605 @@
++
++/*
++ * PS3 Eurus
++ *
++ * Copyright (C) 2011 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#ifndef _PS3_EURUS_H
++#define _PS3_EURUS_H
++
++enum ps3_eurus_cmd_id {
++      PS3_EURUS_CMD_0x1                               = 0x0001,
++      PS3_EURUS_CMD_GET_AP_SSID                       = 0x0003,
++      PS3_EURUS_CMD_SET_AP_SSID                       = 0x0005,
++      PS3_EURUS_CMD_GET_CHANNEL                       = 0x000f,
++      PS3_EURUS_CMD_SET_CHANNEL                       = 0x0011,
++      PS3_EURUS_CMD_SET_ANTENNA                       = 0x0029,
++      PS3_EURUS_CMD_GET_AP_WEP_CONFIG                 = 0x0059,
++      PS3_EURUS_CMD_SET_AP_WEP_CONFIG                 = 0x005b,
++      PS3_EURUS_CMD_0x61                              = 0x0061,
++      PS3_EURUS_CMD_0x65                              = 0x0065,
++      PS3_EURUS_CMD_GET_FW_VERSION                    = 0x0099,
++      PS3_EURUS_CMD_GET_AP_OPMODE                     = 0x00b7,
++      PS3_EURUS_CMD_SET_AP_OPMODE                     = 0x00b9,
++      PS3_EURUS_CMD_0xc5                              = 0x00c5,
++      PS3_EURUS_CMD_GET_AP_WPA_AKM_SUITE              = 0x00c8,
++      PS3_EURUS_CMD_SET_AP_WPA_AKM_SUITE              = 0x00c9,
++      PS3_EURUS_CMD_GET_AP_WPA_GROUP_CIPHER_SUITE     = 0x00cd,
++      PS3_EURUS_CMD_SET_AP_WPA_GROUP_CIPHER_SUITE     = 0x00cf,
++      PS3_EURUS_CMD_GET_AP_WPA_PSK_PASSPHRASE         = 0x00d1,
++      PS3_EURUS_CMD_SET_AP_WPA_PSK_PASSPHRASE         = 0x00d3,
++      PS3_EURUS_CMD_0xd5                              = 0x00d5,
++      PS3_EURUS_CMD_0x127                             = 0x0127,
++      PS3_EURUS_CMD_0x12b                             = 0x012b,
++      PS3_EURUS_CMD_GET_AP_WPA_PSK_BIN                = 0x017b,
++      PS3_EURUS_CMD_SET_AP_WPA_PSK_BIN                = 0x017d,
++      PS3_EURUS_CMD_GET_AP_WPA_PAIRWISE_CIPHER_SUITE  = 0x01bd,
++      PS3_EURUS_CMD_SET_AP_WPA_PAIRWISE_CIPHER_SUITE  = 0x01bf,
++      PS3_EURUS_CMD_0x1d9                             = 0x01d9,
++      PS3_EURUS_CMD_START_AP                          = 0x01dd,
++      PS3_EURUS_CMD_0x1ed                             = 0x01ed,
++      PS3_EURUS_CMD_GET_HW_REVISION                   = 0x01fb,
++      PS3_EURUS_CMD_0x203                             = 0x0203,
++      PS3_EURUS_CMD_0x207                             = 0x0207,
++      PS3_EURUS_CMD_ASSOCIATE                         = 0x1001,
++      PS3_EURUS_CMD_GET_COMMON_CONFIG                 = 0x1003,
++      PS3_EURUS_CMD_SET_COMMON_CONFIG                 = 0x1005,
++      PS3_EURUS_CMD_GET_WEP_CONFIG                    = 0x1013,
++      PS3_EURUS_CMD_SET_WEP_CONFIG                    = 0x1015,
++      PS3_EURUS_CMD_GET_WPA_CONFIG                    = 0x1017,
++      PS3_EURUS_CMD_SET_WPA_CONFIG                    = 0x1019,
++      PS3_EURUS_CMD_0x1025                            = 0x1025,
++      PS3_EURUS_CMD_0x1031                            = 0x1031,
++      PS3_EURUS_CMD_GET_SCAN_RESULTS                  = 0x1033,
++      PS3_EURUS_CMD_START_SCAN                        = 0x1035,
++      PS3_EURUS_CMD_DISASSOCIATE                      = 0x1037,
++      PS3_EURUS_CMD_GET_RSSI                          = 0x103d,
++      PS3_EURUS_CMD_GET_MAC_ADDR                      = 0x103f,
++      PS3_EURUS_CMD_SET_MAC_ADDR                      = 0x1041,
++      PS3_EURUS_CMD_0x104d                            = 0x104d,
++      PS3_EURUS_CMD_0x104f                            = 0x104f,
++      PS3_EURUS_CMD_0x105f                            = 0x105f,
++      PS3_EURUS_CMD_0x1109                            = 0x1109,
++      PS3_EURUS_CMD_0x110b                            = 0x110b,
++      PS3_EURUS_CMD_0x110d                            = 0x110d,
++      PS3_EURUS_CMD_0x1133                            = 0x1133,
++      PS3_EURUS_CMD_0x114b                            = 0x114b,
++      PS3_EURUS_CMD_0x114f                            = 0x114f,
++      PS3_EURUS_CMD_0x115b                            = 0x115b,
++      PS3_EURUS_CMD_0x115d                            = 0x115d,
++      PS3_EURUS_CMD_0x115f                            = 0x115f,
++      PS3_EURUS_CMD_SET_MCAST_ADDR_FILTER             = 0x1161,
++      PS3_EURUS_CMD_CLEAR_MCAST_ADDR_FILTER           = 0x1163,
++      PS3_EURUS_CMD_GET_MCAST_ADDR_FILTER             = 0x1165,
++      PS3_EURUS_CMD_0x116d                            = 0x116d,
++      PS3_EURUS_CMD_0x116f                            = 0x116f,
++      PS3_EURUS_CMD_GET_MAC_ADDR_LIST                 = 0x1117,
++      PS3_EURUS_CMD_0x1171                            = 0x1171,
++      PS3_EURUS_CMD_GET_CHANNEL_INFO                  = 0xfffd,
++};
++
++enum ps3_eurus_cmd_status {
++      PS3_EURUS_CMD_OK                                = 0x0001,
++      PS3_EURUS_CMD_INVALID_LENGTH                    = 0x0002,
++      PS3_EURUS_CMD_UNSUPPORTED                       = 0x0003,
++      PS3_EURUS_CMD_INVALID_PARAMETER                 = 0x0004,
++};
++
++enum ps3_eurus_event_type {
++      PS3_EURUS_EVENT_TYPE_0x8                        = 0x00000008,
++      PS3_EURUS_EVENT_TYPE_0x10                       = 0x00000010,
++      PS3_EURUS_EVENT_TYPE_0x40                       = 0x00000040,
++      PS3_EURUS_EVENT_TYPE_0x80                       = 0x00000080,
++      PS3_EURUS_EVENT_TYPE_0x100                      = 0x00000100,
++      PS3_EURUS_EVENT_TYPE_0x400                      = 0x00000400,
++      PS3_EURUS_EVENT_TYPE_0x80000000                 = 0x80000000
++};
++
++enum ps3_eurus_event_id {
++      /* event type 0x00000008 */
++
++      PS3_EURUS_EVENT_STA_CONNECTED                   = 0x00000010,
++
++      /* event type 0x00000010 */
++
++      PS3_EURUS_EVENT_STA_DISCONNECTED                = 0x00000002,
++      PS3_EURUS_EVENT_AP_STOPPED                      = 0x00000004,
++
++      /* event type 0x00000040 */
++
++      PS3_EURUS_EVENT_DEAUTH                          = 0x00000001,
++
++      /* event type 0x00000080 */
++
++      PS3_EURUS_EVENT_BEACON_LOST                     = 0x00000001,
++      PS3_EURUS_EVENT_CONNECTED                       = 0x00000002,
++      PS3_EURUS_EVENT_SCAN_COMPLETED                  = 0x00000004,
++      PS3_EURUS_EVENT_WPA_CONNECTED                   = 0x00000020,
++      PS3_EURUS_EVENT_WPA_ERROR                       = 0x00000040,
++
++      /* event type 0x00000100 */
++
++      PS3_EURUS_EVENT_0x100_0x2                       = 0x00000002,
++      PS3_EURUS_EVENT_AP_STARTED                      = 0x00000010,
++      PS3_EURUS_EVENT_STA_WPA_CONNECTED               = 0x00000020,
++      PS3_EURUS_EVENT_0x100_0x40                      = 0x00000040,
++
++      /* event type 0x80000000 */
++
++      PS3_EURUS_EVENT_DEVICE_READY                    = 0x00000001,
++};
++
++enum ps3_eurus_ap_opmode {
++      PS3_EURUS_AP_OPMODE_11B                         = 0x00000000,
++      PS3_EURUS_AP_OPMODE_11G                         = 0x00000001,
++      PS3_EURUS_AP_OPMODE_11BG                        = 0x00000002,
++};
++
++enum ps3_eurus_bss_type {
++      PS3_EURUS_BSS_INFRA                             = 0x00,
++      PS3_EURUS_BSS_ADHOC                             = 0x02,
++};
++
++enum ps3_eurus_auth_mode {
++      PS3_EURUS_AUTH_OPEN                             = 0x00,
++      PS3_EURUS_AUTH_SHARED_KEY                       = 0x01,
++};
++
++enum ps3_eurus_opmode {
++      PS3_EURUS_OPMODE_11BG                           = 0x00,
++      PS3_EURUS_OPMODE_11B                            = 0x01,
++      PS3_EURUS_OPMODE_11G                            = 0x02,
++};
++
++enum ps3_eurus_preamble_mode {
++      PS3_EURUS_PREAMBLE_SHORT                        = 0x00,
++      PS3_EURUS_PREAMBLE_LONG                         = 0x01,
++};
++
++enum ps3_eurus_wep_security_mode {
++      PS3_EURUS_WEP_SECURITY_NONE                     = 0x00,
++      PS3_EURUS_WEP_SECURITY_40BIT                    = 0x01,
++      PS3_EURUS_WEP_SECURITY_104BIT                   = 0x02,
++};
++
++enum ps3_eurus_wpa_security_mode {
++      PS3_EURUS_WPA_SECURITY_WPA                      = 0x00,
++      PS3_EURUS_WPA_SECURITY_WPA2                     = 0x01,
++};
++
++enum ps3_eurus_wpa_psk_type {
++      PS3_EURUS_WPA_PSK_PASSPHRASE                    = 0x00,
++      PS3_EURUS_WPA_PSK_BIN                           = 0x01,
++};
++
++enum ps3_eurus_wpa_cipher_suite {
++      PS3_EURUS_WPA_CIPHER_SUITE_WPA_TKIP             = 0x0050f202,
++      PS3_EURUS_WPA_CIPHER_SUITE_WPA_AES              = 0x0050f204,
++      PS3_EURUS_WPA_CIPHER_SUITE_WPA2_TKIP            = 0x000fac02,
++      PS3_EURUS_WPA_CIPHER_SUITE_WPA2_AES             = 0x000fac04,
++};
++
++enum ps3_eurus_wpa_akm_suite {
++      PS3_EURUS_WPA_AKM_SUITE_WPA_PSK                 = 0x0050f202,
++      PS3_EURUS_WPA_AKM_SUITE_WPA2_PSK                = 0x000fac02,
++};
++
++struct ps3_eurus_cmd_hdr {
++      __le16 id;                      /* enum ps3_eurus_cmd_id */
++      __le16 tag;
++      __le16 status;                  /* enum ps3_eurus_cmd_status */
++      __le16 payload_length;
++      u8 res[4];
++} __packed;
++
++struct ps3_eurus_cmd_0x1 {
++      u8 unknown;
++      u8 res[3];
++} __packed;
++
++struct ps3_eurus_cmd_ap_ssid {
++      u8 ssid[32];
++      u8 res[4];
++} __packed;
++
++struct ps3_eurus_cmd_get_channel {
++      u8 unknown[35];
++      __le16 channel;
++} __packed;
++
++struct ps3_eurus_cmd_set_channel {
++      u8 channel;
++} __packed;
++
++struct ps3_eurus_cmd_set_antenna {
++      u8 unknown1;
++      u8 unknown2;
++} __packed;
++
++struct ps3_eurus_cmd_ap_wep_config {
++      u8 unknown1;
++      u8 unknown2;
++      u8 security_mode;       /* enum ps3_eurus_wep_security_mode */
++      u8 unknown3;
++      u8 key[4][18];
++} __packed;
++
++struct ps3_eurus_cmd_0x61 {
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x65 {
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_get_fw_version {
++      u8 version[62]; /* string */
++} __packed;
++
++struct ps3_eurus_cmd_ap_opmode {
++      __le32 opmode;  /* enum ps3_eurus_ap_opmode */
++} __packed;
++
++struct ps3_eurus_cmd_0xc5 {
++      __le32 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_ap_wpa_akm_suite {
++      __be32 suite;   /* enum ps3_eurus_wpa_akm_suite */
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_ap_wpa_group_cipher_suite {
++      __be32 cipher_suite;    /* enum ps3_eurus_wpa_cipher_suite */
++} __packed;
++
++struct ps3_eurus_cmd_ap_wpa_psk_passphrase {
++      u8 passphrase[64];
++} __packed;
++
++struct ps3_eurus_cmd_0xd5 {
++      __le32 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x127 {
++      u8 res[4];
++} __packed;
++
++struct ps3_eurus_cmd_0x12b {
++      u8 res[4];
++} __packed;
++
++struct ps3_eurus_cmd_ap_wpa_psk_bin {
++      u8 enable;
++      u8 psk[32];
++} __packed;
++
++struct ps3_eurus_cmd_ap_wpa_pairwise_cipher_suite {
++      __be32 cipher_suite;    /* enum ps3_eurus_wpa_cipher_suite */
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x1d9 {
++      u8 unknown1;
++      u8 unknown2;
++      u8 res[2];
++} __packed;
++
++struct ps3_eurus_cmd_start_ap {
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x1ed {
++      __le32 unknown1;
++      u8 unknown2;
++      u8 unknown3;
++      u8 unknown4;
++      u8 unknown5;
++      u8 unknown6;
++      u8 unknown7;
++      u8 unknown8;
++} __packed;
++
++struct ps3_eurus_cmd_get_hw_revision {
++      u8 unknown[4];
++} __packed;
++
++struct ps3_eurus_cmd_0x203 {
++      __le32 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x207 {
++      __le32 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_associate {
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_common_config {
++      u8 bss_type;            /* enum ps3_eurus_bss_type */
++      u8 auth_mode;           /* enum ps3_eurus_auth_mode */
++      u8 opmode;              /* enum ps3_eurus_opmode */
++      u8 unknown;
++      u8 bssid[6];
++      __le16 capability;
++      u8 ie[0];
++} __packed;
++
++struct ps3_eurus_cmd_wep_config {
++      u8 unknown1;
++      u8 security_mode;               /* enum ps3_eurus_wep_security_mode */
++      __le16 unknown2;
++      u8 key[4][16];
++} __packed;
++
++struct ps3_eurus_cmd_wpa_config {
++      u8 unknown;
++      u8 security_mode;               /* enum ps3_eurus_wpa_security_mode */
++      u8 psk_type;                    /* enum ps3_eurus_wpa_psk_type */
++      u8 psk[64];
++      __be32 group_cipher_suite;      /* enum ps3_eurus_wpa_cipher_suite */
++      __be32 pairwise_cipher_suite;   /* enum ps3_eurus_wpa_cipher_suite */
++      __be32 akm_suite;               /* enum ps3_eurus_wpa_akm_suite */
++} __packed;
++
++struct ps3_eurus_cmd_0x1025 {
++      u8 preamble_mode;       /* enum ps3_eurus_preamble_mode */
++      u8 res[3];
++} __packed;
++
++struct ps3_eurus_cmd_0x1031 {
++      u8 unknown1;
++      u8 unknown2;
++} __packed;
++
++struct ps3_eurus_scan_result {
++      __le16 length;
++      u8 bssid[6];
++      u8 rssi;
++      __le64 timestamp;
++      __le16 beacon_period;           /* in msec */
++      __le16 capability;
++      u8 ie[0];
++} __packed;
++
++#define PS3_EURUS_SCAN_RESULTS_MAXSIZE        0x5b0
++
++struct ps3_eurus_cmd_get_scan_results {
++      u8 count;
++      struct ps3_eurus_scan_result result[0];
++} __packed;
++
++struct ps3_eurus_cmd_start_scan {
++      u8 unknown1;
++      u8 unknown2;
++      __le16 channel_dwell;   /* in msec */
++      u8 res[6];
++      u8 ie[0];
++} __packed;
++
++struct ps3_eurus_cmd_disassociate {
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_get_rssi {
++      u8 res[10];
++      u8 rssi;
++} __packed;
++
++struct ps3_eurus_cmd_get_mac_addr {
++      u8 unknown;
++      u8 mac_addr[6];
++} __packed;
++
++struct ps3_eurus_cmd_set_mac_addr {
++      u8 mac_addr[6];
++} __packed;
++
++struct ps3_eurus_cmd_0x104d {
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x104f {
++      u8 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x105f {
++      __le16 channel_info;
++      u8 mac_addr[6];
++      u8 unknown1;
++      u8 unknown2;
++} __packed;
++
++struct ps3_eurus_cmd_0x1109 {
++      __le16 unknown1;
++      __le16 unknown2;
++      __le16 unknown3;
++      __le16 unknown4;
++      __le16 unknown5;
++      __le16 unknown6;
++      __le16 unknown7;
++      u8 unknown8;
++      u8 res;
++      u8 unknown9;
++      u8 unknown10;
++      __le16 unknown11;
++      __le16 unknown12;
++} __packed;
++
++struct ps3_eurus_cmd_0x110b {
++      __le32 unknown1;
++      u8 res[4];
++      __le32 unknown2;
++} __packed;
++
++struct ps3_eurus_cmd_0x110d {
++      u8 res1[12];
++      __le32 unknown1;
++      __le32 unknown2;
++      __le32 unknown3;
++      __le32 unknown4;
++      __le32 unknown5;
++      __le32 unknown6;
++      __le32 unknown7;
++      u8 res2[88];
++} __packed;
++
++struct ps3_eurus_cmd_0x1133 {
++      u8 unknown1;
++      u8 unknown2;
++      u8 unknown3;
++      u8 unknown4;
++      __le32 unknown5;
++      u8 res[6];
++} __packed;
++
++struct ps3_eurus_cmd_0x114f {
++      u8 res[1304];
++} __packed;
++
++struct ps3_eurus_cmd_0x115b {
++      __le16 unknown1;
++      __le16 unknown2;
++      u8 mac_addr[6];
++      u8 res[84];
++} __packed;
++
++struct ps3_eurus_cmd_mcast_addr_filter {
++      __le32 word[8];
++} __packed;
++
++struct ps3_eurus_cmd_0x116d {
++      __le32 unknown;
++} __packed;
++
++struct ps3_eurus_cmd_0x116f {
++      __le32 unknown;
++} __packed;
++
++#define PS3_EURUS_MAC_ADDR_LIST_MAXSIZE       0xc2
++
++struct ps3_eurus_cmd_get_mac_addr_list {
++      __le16 count;   /* number of MAC addresses */
++      u8 mac_addr[0];
++} __packed;
++
++struct ps3_eurus_cmd_get_channel_info {
++      u16 channel_info;
++} __packed;
++
++struct ps3_eurus_event_hdr {
++      __le32 type;                    /* enum ps3_eurus_event_type */
++      __le32 id;                      /* enum ps3_eurus_event_id */
++      __le32 timestamp;
++      __le32 payload_length;
++      __le32 unknown;
++} __packed;
++
++struct ps3_eurus_event {
++      struct ps3_eurus_event_hdr hdr;
++      u8 payload[44];
++} __packed;
++
++/*
++ * ps3_eurus_rssi2percentage
++ */
++static inline u8 ps3_eurus_rssi2percentage(u8 rssi)
++{
++      if (rssi > 89)
++              return 1;
++      else if (rssi < 50)
++              return 100;
++      else
++              return ((90 - rssi) * 100) / 40;
++}
++
++#define PS3_EURUS_MCAST_ADDR_HASH2VAL(h)      (1 << ((h) & 0x1f))
++#define PS3_EURUS_MCAST_ADDR_HASH2POS(h)      (((h) >> 5) & 0x7)
++
++/*
++ * ps3_eurus_mcast_addr_hash
++ */
++static inline u8 ps3_eurus_mcast_addr_hash(const u8 mac_addr[ETH_ALEN])
++{
++      u8 buf[ETH_ALEN];
++      u32 h;
++      unsigned int i, j;
++
++      memcpy(buf, mac_addr, ETH_ALEN);
++
++      /* reverse bits in each byte */
++
++      for (i = 0; i < ETH_ALEN; i++) {
++              buf[i] = (buf[i] >> 4) | ((buf[i] & 0xf) << 4);
++              buf[i] = ((buf[i] & 0xcc) >> 2) | ((buf[i] & 0x33) << 2);
++              buf[i] = ((buf[i] & 0xaa) >> 1) | ((buf[i] & 0x55) << 1);
++      }
++
++        h = 0xffffffff;
++
++        for (i = 0; i < ETH_ALEN; i++) {
++                h = (((unsigned int) buf[i]) << 24) ^ h;
++
++                for (j = 0; j < 8; j++) {
++                        if (((int) h) >= 0) {
++                                h = h << 1;
++                        } else {
++                                h = (h << 1) ^ 0x4c10000;
++                                h = h ^ 0x1db7;
++                        }
++                }
++        }
++
++        h = ((h >> 24) & 0xf8) | (h & 0x7);
++
++        return (h & 0xff);
++}
++
++/*
++ * ps3_eurus_make_cmd_0x1109
++ */
++static inline void ps3_eurus_make_cmd_0x1109(struct ps3_eurus_cmd_0x1109 *cmd_0x1109,
++      u8 arg1, u16 arg2, u16 arg3, u16 arg4, u16 arg5)
++{
++      memset(cmd_0x1109, 0, sizeof(*cmd_0x1109));
++
++      cmd_0x1109->unknown1 = cpu_to_le16(0x1);
++      cmd_0x1109->unknown8 = arg1;
++
++      if (arg1 == 0x0) {
++              cmd_0x1109->unknown6 = cpu_to_le16(0xa);
++      } else if (arg1 == 0x1) {
++              cmd_0x1109->unknown2 = cpu_to_le16(arg2);
++              cmd_0x1109->unknown3 = cpu_to_le16(arg3);
++              cmd_0x1109->unknown4 = cpu_to_le16(arg5);
++
++              if (arg2 == 0x0)
++                      cmd_0x1109->unknown5 = cpu_to_le16(0x6);
++              else
++                      cmd_0x1109->unknown5 = cpu_to_le16(0x2);
++
++              cmd_0x1109->unknown7 = cpu_to_le16(arg4);
++              cmd_0x1109->unknown9 = 0xff;
++              cmd_0x1109->unknown10 = 0xff;
++              cmd_0x1109->unknown11 = 0xffff;
++              cmd_0x1109->unknown12 = 0xffff;
++      }
++}
++
++#endif
+--- /dev/null  2012-11-01 00:09:58.397610712 -0800
++++ b/drivers/net/wireless/ps3jupiter/ps3_jupiter.h    2012-11-01 03:22:23.000000000 -0800
+@@ -0,0 +1,34 @@
++
++/*
++ * PS3 Jupiter
++ *
++ * Copyright (C) 2011 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#ifndef _PS3_JUPITER_H
++#define _PS3_JUPITER_H
++
++int ps3_jupiter_register_event_listener(struct notifier_block *listener);
++
++int ps3_jupiter_unregister_event_listener(struct notifier_block *listener);
++
++int ps3_jupiter_exec_eurus_cmd(enum ps3_eurus_cmd_id cmd,
++      void *payload, unsigned int payload_length,
++      unsigned int *response_status,
++      unsigned int *response_length, void *response);
++
++#endif
+--- /dev/null  2012-11-01 00:09:58.397610712 -0800
++++ b/drivers/net/wireless/ps3jupiter/ps3_jupiter.c    2012-11-01 03:22:27.000000000 -0800
+@@ -0,0 +1,1245 @@
++
++/*
++ * PS3 Jupiter
++ *
++ * Copyright (C) 2011, 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++#include <linux/notifier.h>
++
++#include <linux/etherdevice.h>
++#include <linux/if_ether.h>
++
++#include <asm/byteorder.h>
++#include <asm/ps3.h>
++#include <asm/lv1call.h>
++
++#include "ps3_eurus.h"
++#include "ps3_jupiter.h"
++
++#define PS3_JUPITER_EP                        0x5
++
++#define PS3_JUPITER_IRQ_BUFSIZE               2048
++#define PS3_JUPITER_CMD_BUFSIZE               2048
++
++#define LV1_SB_BUS_ID                 0x1
++#define LV1_GELIC_DEV_ID              0x0
++#define LV1_GET_MAC_ADDRESS           0x1
++#define LV1_GET_CHANNEL_INFO          0x6
++
++enum ps3_jupiter_pkt_type {
++      PS3_JUPITER_PKT_CMD     = 6,
++      PS3_JUPITER_PKT_EVENT   = 8,
++};
++
++struct ps3_jupiter_dev {
++      struct usb_device *udev;
++      struct urb *irq_urb, *cmd_urb;
++      void *irq_buf, *cmd_buf;
++
++      u16 cmd_tag, eurus_cmd, eurus_tag;
++      struct completion cmd_done_comp;
++      spinlock_t cmd_lock;
++      int cmd_busy, cmd_err;
++
++      struct workqueue_struct *event_queue;
++      struct delayed_work event_work;
++      struct blocking_notifier_head event_listeners;
++      struct list_head event_list;
++      spinlock_t event_list_lock;
++
++      struct notifier_block event_listener;
++      struct completion event_comp;
++
++      unsigned char mac_addr[ETH_ALEN];
++      u64 channel_info;
++
++      u16 dev_status;
++      int dev_ready;
++};
++
++struct ps3_jupiter_pkt_hdr {
++      u8 unknown1;
++      u8 unknown2;
++      u8 type;
++} __packed;
++
++struct ps3_jupiter_cmd_hdr {
++      u8 unknown1;
++      __le16 unknown2;
++      u8 res1[2];
++      __le16 tag;
++      u8 res2[14];
++} __packed;
++
++struct ps3_jupiter_event_hdr {
++      u8 count;
++} __packed;
++
++struct ps3_jupiter_list_event {
++      struct list_head list;
++      struct ps3_eurus_event event;
++};
++
++static struct ps3_jupiter_dev *ps3jd;
++
++static unsigned char ps3_jupiter_devkey[] = {
++      0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
++      0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
++};
++
++/*
++ * ps3_jupiter_event_worker
++ */
++static void ps3_jupiter_event_worker(struct work_struct *work)
++{
++      struct ps3_jupiter_dev *jd = container_of(work, struct ps3_jupiter_dev, event_work.work);
++      struct ps3_jupiter_list_event *list_event;
++      unsigned long flags;
++
++      /* dispatch received events to each listener */
++
++      while (1) {
++              spin_lock_irqsave(&jd->event_list_lock, flags);
++
++              if (list_empty(&jd->event_list)) {
++                      spin_unlock_irqrestore(&jd->event_list_lock, flags);
++                      break;
++              }
++
++              list_event = list_entry(jd->event_list.next, struct ps3_jupiter_list_event, list);
++              list_del(&list_event->list);
++
++              spin_unlock_irqrestore(&jd->event_list_lock, flags);
++
++              blocking_notifier_call_chain(&jd->event_listeners, 0, &list_event->event);
++
++              kfree(list_event);
++      }
++}
++
++/*
++ * ps3_jupiter_event_irq
++ */
++static void ps3_jupiter_event_irq(struct ps3_jupiter_dev *jd,
++      void *buf, unsigned int length)
++{
++      struct usb_device *udev = jd->udev;
++      struct ps3_jupiter_pkt_hdr *pkt_hdr;
++      struct ps3_jupiter_event_hdr *event_hdr;
++      struct ps3_jupiter_list_event *list_event;
++      unsigned long flags;
++      int i;
++
++      dev_dbg(&udev->dev, "got event IRQ packet\n");
++
++      if (length < sizeof(*pkt_hdr) + sizeof(*event_hdr)) {
++              dev_err(&udev->dev, "got event IRQ packet with invalid length (%d)\n",
++                  length);
++              return;
++      }
++
++      pkt_hdr = (struct ps3_jupiter_pkt_hdr *) buf;
++      event_hdr = (struct ps3_jupiter_event_hdr *) (pkt_hdr + 1);
++
++      if (length < sizeof(*pkt_hdr) + sizeof(*event_hdr) +
++              event_hdr->count * sizeof(struct ps3_eurus_event)) {
++              dev_err(&udev->dev, "got event IRQ packet with invalid length (%d)\n",
++                  length);
++              return;
++      }
++
++      dev_dbg(&udev->dev, "got %d event(s)\n", event_hdr->count);
++
++      for (i = 0; i < event_hdr->count; i++) {
++              list_event = kmalloc(sizeof(*list_event), GFP_ATOMIC);
++              if (!list_event) {
++                      dev_err(&udev->dev, "could not allocate memory for new event\n");
++                      continue;
++              }
++
++              memcpy(&list_event->event, (unsigned char *) event_hdr + sizeof(*event_hdr) +
++                  i * sizeof(struct ps3_eurus_event), sizeof(struct ps3_eurus_event));
++              list_event->event.hdr.type = le32_to_cpu(list_event->event.hdr.type);
++              list_event->event.hdr.id = le32_to_cpu(list_event->event.hdr.id);
++              list_event->event.hdr.timestamp = le32_to_cpu(list_event->event.hdr.timestamp);
++              list_event->event.hdr.payload_length = le32_to_cpu(list_event->event.hdr.payload_length);
++              list_event->event.hdr.unknown = le32_to_cpu(list_event->event.hdr.unknown);
++
++              spin_lock_irqsave(&jd->event_list_lock, flags);
++              list_add_tail(&list_event->list, &jd->event_list);
++              spin_unlock_irqrestore(&jd->event_list_lock, flags);
++      }
++
++      if (event_hdr->count)
++              queue_delayed_work(jd->event_queue, &jd->event_work, 0);
++}
++
++/*
++ * ps3_jupiter_cmd_irq
++ */
++static void ps3_jupiter_cmd_irq(struct ps3_jupiter_dev *jd,
++      void *buf, unsigned int length)
++{
++      struct usb_device *udev = jd->udev;
++      struct ps3_jupiter_pkt_hdr *pkt_hdr;
++      struct ps3_jupiter_cmd_hdr *cmd_hdr;
++      struct ps3_eurus_cmd_hdr *eurus_cmd_hdr;
++      u16 cmd_tag, eurus_cmd, eurus_tag, payload_length;
++
++      dev_dbg(&udev->dev, "got command IRQ packet\n");
++
++      if (length < sizeof(*pkt_hdr) + sizeof(*cmd_hdr) + sizeof(*eurus_cmd_hdr)) {
++              dev_err(&udev->dev, "got command IRQ packet with invalid length (%d)\n",
++                  length);
++              return;
++      }
++
++      pkt_hdr = (struct ps3_jupiter_pkt_hdr *) buf;
++      cmd_hdr = (struct ps3_jupiter_cmd_hdr *) (pkt_hdr + 1);
++      eurus_cmd_hdr = (struct ps3_eurus_cmd_hdr *) (cmd_hdr + 1);
++      payload_length = le16_to_cpu(eurus_cmd_hdr->payload_length);
++
++      if (length < sizeof(*pkt_hdr) + sizeof(*cmd_hdr) + sizeof(*eurus_cmd_hdr) + payload_length) {
++              dev_err(&udev->dev, "got command IRQ packet with invalid length (%d)\n",
++                  length);
++              return;
++      }
++
++      cmd_tag = le16_to_cpu(cmd_hdr->tag);
++
++      if (jd->cmd_tag != cmd_tag)
++              dev_err(&udev->dev, "got command IRQ packet with invalid command tag, "
++                  "got (0x%04x), expected (0x%04x)\n", cmd_tag, jd->cmd_tag);
++
++      eurus_cmd = le16_to_cpu(eurus_cmd_hdr->id);
++
++      if ((jd->eurus_cmd + 1) != eurus_cmd)
++              dev_err(&udev->dev, "got command IRQ packet with invalid EURUS command, "
++                  "got (0x%04x), expected (0x%04x)\n", eurus_cmd, jd->eurus_cmd);
++
++      eurus_tag = le16_to_cpu(eurus_cmd_hdr->tag);
++
++      if (jd->eurus_tag != eurus_tag)
++              dev_err(&udev->dev, "got command IRQ packet with invalid EURUS tag, "
++                  "got (0x%04x), expected (0x%04x)\n", eurus_tag, jd->eurus_tag);
++
++      memcpy(jd->cmd_buf, buf, length);
++
++      jd->cmd_err = 0;
++      complete(&jd->cmd_done_comp);
++}
++
++/*
++ * ps3_jupiter_irq_urb_complete
++ */
++static void ps3_jupiter_irq_urb_complete(struct urb *urb)
++{
++      struct ps3_jupiter_dev *jd = urb->context;
++      struct usb_device *udev = jd->udev;
++      struct ps3_jupiter_pkt_hdr *pkt_hdr;
++      int err;
++
++      dev_dbg(&udev->dev, "IRQ URB completed (%d)\n", urb->status);
++
++      switch (urb->status) {
++      case 0:
++              if (urb->actual_length < sizeof(*pkt_hdr)) {
++                      dev_err(&udev->dev, "got IRQ packet with invalid length (%d)\n",
++                          urb->actual_length);
++                      break;
++              }
++
++              pkt_hdr = (struct ps3_jupiter_pkt_hdr *) jd->irq_buf;
++
++              switch (pkt_hdr->type) {
++              case PS3_JUPITER_PKT_CMD:
++                      ps3_jupiter_cmd_irq(jd, pkt_hdr, urb->actual_length);
++              break;
++              case PS3_JUPITER_PKT_EVENT:
++                      ps3_jupiter_event_irq(jd, pkt_hdr, urb->actual_length);
++              break;
++              default:
++                      dev_err(&udev->dev, "got unknown IRQ packet type (%d)\n",
++                          pkt_hdr->type);
++              }
++      break;
++      case -EINPROGRESS:
++              /* ignore */
++      break;
++      case -ECONNRESET:
++      case -ENOENT:
++      case -ESHUTDOWN:
++      case -ENODEV:
++      return;
++      default:
++              dev_err(&udev->dev, "IRQ URB failed (%d)\n", urb->status);
++      }
++
++      err = usb_submit_urb(jd->irq_urb, GFP_ATOMIC);
++      if (err)
++              dev_err(&udev->dev, "could not submit IRQ URB (%d)\n", err);
++}
++
++/*
++ * ps3_jupiter_cmd_urb_complete
++ */
++static void ps3_jupiter_cmd_urb_complete(struct urb *urb)
++{
++      struct ps3_jupiter_dev *jd = urb->context;
++      struct usb_device *udev = jd->udev;
++
++      dev_dbg(&udev->dev, "command URB completed (%d)\n", urb->status);
++
++      switch (urb->status) {
++      case 0:
++              /* success */
++      break;
++      case -EINPROGRESS:
++              /* ignore */
++      break;
++      case -ECONNRESET:
++      case -ENOENT:
++      case -ESHUTDOWN:
++      case -ENODEV:
++      default:
++              dev_err(&udev->dev, "command URB failed (%d)\n", urb->status);
++              jd->cmd_err = urb->status;
++              complete(&jd->cmd_done_comp);
++      }
++}
++
++/*
++ * _ps3_jupiter_register_event_listener
++ */
++static int _ps3_jupiter_register_event_listener(struct ps3_jupiter_dev *jd,
++      struct notifier_block *listener)
++{
++      BUG_ON(!jd);
++
++      return blocking_notifier_chain_register(&jd->event_listeners, listener);
++}
++
++/*
++ * ps3_jupiter_register_event_listener
++ */
++int ps3_jupiter_register_event_listener(struct notifier_block *listener)
++{
++      struct ps3_jupiter_dev *jd = ps3jd;
++      int err;
++
++      if (!jd || !jd->dev_ready)
++              return -ENODEV;
++
++      err = _ps3_jupiter_register_event_listener(jd, listener);
++
++      return err;
++}
++
++EXPORT_SYMBOL_GPL(ps3_jupiter_register_event_listener);
++
++/*
++ * _ps3_jupiter_unregister_event_listener
++ */
++static int _ps3_jupiter_unregister_event_listener(struct ps3_jupiter_dev *jd,
++      struct notifier_block *listener)
++{
++      BUG_ON(!jd);
++
++      return blocking_notifier_chain_unregister(&jd->event_listeners, listener);
++}
++
++/*
++ * ps3_jupiter_unregister_event_listener
++ */
++int ps3_jupiter_unregister_event_listener(struct notifier_block *listener)
++{
++      struct ps3_jupiter_dev *jd = ps3jd;
++      int err;
++
++      if (!jd || !jd->dev_ready)
++              return -ENODEV;
++
++      err = _ps3_jupiter_unregister_event_listener(jd, listener);
++
++      return err;
++}
++
++EXPORT_SYMBOL_GPL(ps3_jupiter_unregister_event_listener);
++
++/*
++ * _ps3_jupiter_exec_eurus_cmd
++ */
++static int _ps3_jupiter_exec_eurus_cmd(struct ps3_jupiter_dev *jd,
++      enum ps3_eurus_cmd_id cmd,
++      void *payload, unsigned int payload_length,
++      unsigned int *response_status,
++      unsigned int *response_length, void *response)
++{
++      struct usb_device *udev = jd->udev;
++      struct ps3_jupiter_pkt_hdr *pkt_hdr;
++      struct ps3_jupiter_cmd_hdr *cmd_hdr;
++      struct ps3_eurus_cmd_hdr *eurus_cmd_hdr;
++      struct ps3_eurus_cmd_get_channel_info *eurus_cmd_get_channel_info;
++      u16 status;
++      unsigned long flags;
++      int err;
++
++      BUG_ON(!jd);
++
++      if (!payload && payload_length)
++              return -EINVAL;
++
++      spin_lock_irqsave(&jd->cmd_lock, flags);
++
++      if (jd->cmd_busy) {
++              spin_unlock_irqrestore(&jd->cmd_lock, flags);
++              dev_dbg(&udev->dev,
++                  "trying to execute multiple commands at the same time\n");
++              return -EAGAIN;
++      }
++
++      jd->cmd_busy = 1;
++
++      spin_unlock_irqrestore(&jd->cmd_lock, flags);
++
++      dev_dbg(&udev->dev, "EURUS command 0x%04x payload length %d\n",
++          cmd, payload_length);
++
++      /* internal commands */
++
++      if (cmd == PS3_EURUS_CMD_GET_CHANNEL_INFO) {
++              if (payload_length < sizeof(*eurus_cmd_get_channel_info)) {
++                      err = -EINVAL;
++                      goto done;
++              }
++
++              if (response_status)
++                      *response_status = PS3_EURUS_CMD_OK;
++
++              if (response_length && response) {
++                      *response_length = sizeof(*eurus_cmd_get_channel_info);
++                      eurus_cmd_get_channel_info = (struct ps3_eurus_cmd_get_channel_info *) response;
++                      memset(eurus_cmd_get_channel_info, 0, sizeof(*eurus_cmd_get_channel_info));
++                      eurus_cmd_get_channel_info->channel_info = jd->channel_info >> 48;
++              }
++
++              err = 0;
++
++              goto done;
++      }
++
++      pkt_hdr = (struct ps3_jupiter_pkt_hdr *) jd->cmd_buf;
++      memset(pkt_hdr, 0, sizeof(*pkt_hdr));
++      pkt_hdr->unknown1 = 1;
++      pkt_hdr->unknown2 = 1;
++      pkt_hdr->type = PS3_JUPITER_PKT_CMD;
++
++      cmd_hdr = (struct ps3_jupiter_cmd_hdr *) (pkt_hdr + 1);
++      memset(cmd_hdr, 0, sizeof(*cmd_hdr));
++      jd->cmd_tag++;
++      cmd_hdr->unknown1 = 0;
++      cmd_hdr->unknown2 = cpu_to_le16(1);
++      cmd_hdr->tag = cpu_to_le16(jd->cmd_tag);
++
++      eurus_cmd_hdr = (struct ps3_eurus_cmd_hdr *) (cmd_hdr + 1);
++      memset(eurus_cmd_hdr, 0, sizeof(*eurus_cmd_hdr));
++      jd->eurus_cmd = cmd;
++      eurus_cmd_hdr->id = cpu_to_le16(cmd);
++      jd->eurus_tag++;
++      eurus_cmd_hdr->tag = cpu_to_le16(jd->eurus_tag);
++      eurus_cmd_hdr->status = cpu_to_le16(0xa);
++      eurus_cmd_hdr->payload_length = cpu_to_le16(payload_length);
++
++      if (payload_length)
++              memcpy(eurus_cmd_hdr + 1, payload, payload_length);
++
++      init_completion(&jd->cmd_done_comp);
++
++      usb_fill_int_urb(jd->cmd_urb, udev, usb_sndintpipe(udev, PS3_JUPITER_EP),
++          jd->cmd_buf, sizeof(*pkt_hdr) + sizeof(*cmd_hdr) + sizeof(*eurus_cmd_hdr) + payload_length,
++          ps3_jupiter_cmd_urb_complete, jd, 1);
++
++      err = usb_submit_urb(jd->cmd_urb, GFP_KERNEL);
++      if (err) {
++              dev_err(&udev->dev, "could not submit command URB (%d)\n", err);
++              goto done;
++      }
++
++      err = wait_for_completion_timeout(&jd->cmd_done_comp, HZ);
++      if (!err) {
++              err = -ETIMEDOUT;
++              goto done;
++      }
++
++      err = jd->cmd_err;
++      if (!err) {
++              status = le16_to_cpu(eurus_cmd_hdr->status);
++
++              if (response_status)
++                      *response_status = status;
++
++              if (response_length && response) {
++                      *response_length = le16_to_cpu(eurus_cmd_hdr->payload_length);
++                      memcpy(response, eurus_cmd_hdr + 1, *response_length);
++              }
++
++              if (status != PS3_EURUS_CMD_OK)
++                      dev_err(&udev->dev, "EURUS command 0x%04x status (0x%04x)\n", cmd, status);
++      }
++
++done:
++
++      if (err)
++              dev_err(&udev->dev, "EURUS command 0x%04x failed (%d)\n", cmd, err);
++
++      jd->cmd_busy = 0;
++
++      return err;
++}
++
++/*
++ * _ps3_jupiter_exec_eurus_cmd
++ */
++int ps3_jupiter_exec_eurus_cmd(enum ps3_eurus_cmd_id cmd,
++      void *payload, unsigned int payload_length,
++      unsigned int *response_status,
++      unsigned int *response_length, void *response)
++{
++      struct ps3_jupiter_dev *jd = ps3jd;
++      int err;
++
++      if (!jd || !jd->dev_ready)
++              return -ENODEV;
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, cmd, payload, payload_length,
++          response_status, response_length, response);
++
++      return err;
++}
++
++EXPORT_SYMBOL_GPL(ps3_jupiter_exec_eurus_cmd);
++
++/*
++ * ps3_jupiter_create_event_worker
++ */
++static int ps3_jupiter_create_event_worker(struct ps3_jupiter_dev *jd)
++{
++      jd->event_queue = create_singlethread_workqueue("ps3_jupiter_event");
++      if (!jd->event_queue)
++              return -ENOMEM;
++
++      INIT_DELAYED_WORK(&jd->event_work, ps3_jupiter_event_worker);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_destroy_event_worker
++ */
++static void ps3_jupiter_destroy_event_worker(struct ps3_jupiter_dev *jd)
++{
++      if (jd->event_queue) {
++              cancel_delayed_work(&jd->event_work);
++              flush_workqueue(jd->event_queue);
++              destroy_workqueue(jd->event_queue);
++              jd->event_queue = NULL;
++      }
++}
++
++/*
++ * ps3_jupiter_free_event_list
++ */
++static void ps3_jupiter_free_event_list(struct ps3_jupiter_dev *jd)
++{
++      struct ps3_jupiter_list_event *event, *tmp;
++
++      list_for_each_entry_safe(event, tmp, &jd->event_list, list) {
++              list_del(&event->list);
++              kfree(event);
++      }
++}
++
++/*
++ * ps3_jupiter_alloc_urbs
++ */
++static int ps3_jupiter_alloc_urbs(struct ps3_jupiter_dev *jd)
++{
++      struct usb_device *udev = jd->udev;
++
++      jd->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
++      if (!jd->irq_urb)
++              return -ENOMEM;
++
++      jd->irq_buf = usb_alloc_coherent(udev, PS3_JUPITER_IRQ_BUFSIZE,
++          GFP_KERNEL, &jd->irq_urb->transfer_dma);
++      if (!jd->irq_buf)
++              return -ENOMEM;
++
++      usb_fill_int_urb(jd->irq_urb, udev, usb_rcvintpipe(udev, PS3_JUPITER_EP),
++          jd->irq_buf, PS3_JUPITER_IRQ_BUFSIZE, ps3_jupiter_irq_urb_complete, jd, 1);
++      jd->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++      jd->cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
++      if (!jd->cmd_urb)
++              return -ENOMEM;
++
++      jd->cmd_buf = usb_alloc_coherent(udev, PS3_JUPITER_CMD_BUFSIZE,
++          GFP_KERNEL, &jd->cmd_urb->transfer_dma);
++      if (!jd->cmd_buf)
++              return -ENOMEM;
++
++      usb_fill_int_urb(jd->cmd_urb, udev, usb_sndintpipe(udev, PS3_JUPITER_EP),
++          jd->cmd_buf, PS3_JUPITER_CMD_BUFSIZE, ps3_jupiter_cmd_urb_complete, jd, 1);
++      jd->cmd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_free_urbs
++ */
++static void ps3_jupiter_free_urbs(struct ps3_jupiter_dev *jd)
++{
++      struct usb_device *udev = jd->udev;
++
++      if (jd->irq_urb) {
++              usb_kill_urb(jd->irq_urb);
++
++              if (jd->irq_buf)
++                      usb_free_coherent(udev, PS3_JUPITER_IRQ_BUFSIZE,
++                          jd->irq_buf, jd->irq_urb->transfer_dma);
++
++              usb_free_urb(jd->irq_urb);
++      }
++
++      if (jd->cmd_urb) {
++              usb_kill_urb(jd->cmd_urb);
++
++              if (jd->cmd_buf)
++                      usb_free_coherent(udev, PS3_JUPITER_CMD_BUFSIZE,
++                          jd->cmd_buf, jd->cmd_urb->transfer_dma);
++
++              usb_free_urb(jd->cmd_urb);
++      }
++}
++
++/*
++ * ps3_jupiter_dev_auth
++ */
++static int ps3_jupiter_dev_auth(struct ps3_jupiter_dev *jd)
++{
++      struct usb_device *udev = jd->udev;
++      void *buf;
++      int err;
++
++      buf = kmalloc(sizeof(ps3_jupiter_devkey), GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      memcpy(buf, ps3_jupiter_devkey, sizeof(ps3_jupiter_devkey));
++
++      err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
++          0x1, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, 0x9, 0x0,
++          buf, sizeof(ps3_jupiter_devkey), USB_CTRL_SET_TIMEOUT);
++      if (err < 0) {
++              dev_dbg(&udev->dev, "could not send device key (%d)\n", err);
++              return err;
++      }
++
++      kfree(buf);
++
++      err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
++          0x0, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0x2, 0x0,
++          &jd->dev_status, sizeof(jd->dev_status), USB_CTRL_GET_TIMEOUT);
++      if (err < 0) {
++              dev_dbg(&udev->dev, "could not read device status (%d)\n", err);
++              return err;
++      }
++
++      dev_info(&udev->dev, "device status (0x%04x)\n", jd->dev_status);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_event_handler
++ */
++static int ps3_jupiter_event_handler(struct notifier_block *n,
++      unsigned long val, void *v)
++{
++      struct ps3_jupiter_dev *jd = container_of(n, struct ps3_jupiter_dev, event_listener);
++      struct usb_device *udev = jd->udev;
++      struct ps3_eurus_event *event = v;
++
++      dev_dbg(&udev->dev, "got event (0x%08x 0x%08x 0x%08x 0x%08x 0x%08x)\n",
++          event->hdr.type, event->hdr.id, event->hdr.timestamp, event->hdr.payload_length,
++          event->hdr.unknown);
++
++      if (event->hdr.type == PS3_EURUS_EVENT_TYPE_0x400) {
++              if ((event->hdr.id == 0x8) || (event->hdr.id == 0x10))
++                      complete(&jd->event_comp);
++      }
++
++      return NOTIFY_OK;
++}
++
++/*
++ * ps3_jupiter_dev_init
++ */
++static int ps3_jupiter_dev_init(struct ps3_jupiter_dev *jd)
++{
++      struct usb_device *udev = jd->udev;
++      struct ps3_eurus_cmd_0x114f *eurus_cmd_0x114f;
++      struct ps3_eurus_cmd_0x116f *eurus_cmd_0x116f;
++      struct ps3_eurus_cmd_0x115b *eurus_cmd_0x115b;
++      const u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++      u8 h;
++      struct ps3_eurus_cmd_mcast_addr_filter *eurus_cmd_mcast_addr_filter;
++      struct ps3_eurus_cmd_0x110d *eurus_cmd_0x110d;
++      struct ps3_eurus_cmd_0x1031 *eurus_cmd_0x1031;
++      struct ps3_eurus_cmd_set_mac_addr *eurus_cmd_set_mac_addr;
++      struct ps3_eurus_cmd_set_antenna *eurus_cmd_set_antenna;
++      struct ps3_eurus_cmd_0x110b *eurus_cmd_0x110b;
++      struct ps3_eurus_cmd_0x1109 *eurus_cmd_0x1109;
++      struct ps3_eurus_cmd_0x207 *eurus_cmd_0x207;
++      struct ps3_eurus_cmd_0x203 *eurus_cmd_0x203;
++      struct ps3_eurus_cmd_0x105f *eurus_cmd_0x105f;
++      struct ps3_eurus_cmd_get_fw_version *eurus_cmd_get_fw_version;
++      unsigned char *buf;
++      unsigned int status, response_length;
++      int err;
++
++      buf = kmalloc(PS3_JUPITER_CMD_BUFSIZE, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      /* state 1 */
++
++      eurus_cmd_0x114f = (struct ps3_eurus_cmd_0x114f *) buf;
++      memset(eurus_cmd_0x114f, 0, sizeof(*eurus_cmd_0x114f));
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x114f,
++          eurus_cmd_0x114f, sizeof(*eurus_cmd_0x114f), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      /* do not check command status here !!! */
++
++      /* state 2 */
++
++      init_completion(&jd->event_comp);
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x1171, NULL, 0, &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 3 */
++
++      err = wait_for_completion_timeout(&jd->event_comp, HZ);
++      if (!err) {
++              err = -ETIMEDOUT;
++              goto done;
++      }
++
++      /* state 4 */
++
++      eurus_cmd_0x116f = (struct ps3_eurus_cmd_0x116f *) buf;
++      memset(eurus_cmd_0x116f, 0, sizeof(*eurus_cmd_0x116f));
++      eurus_cmd_0x116f->unknown = cpu_to_le32(0x1);
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x116f,
++          eurus_cmd_0x116f, sizeof(*eurus_cmd_0x116f), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 5 */
++
++      eurus_cmd_0x115b = (struct ps3_eurus_cmd_0x115b *) buf;
++      memset(eurus_cmd_0x115b, 0, sizeof(*eurus_cmd_0x115b));
++      eurus_cmd_0x115b->unknown1 = cpu_to_le16(0x1);
++      eurus_cmd_0x115b->unknown2 = cpu_to_le16(0x0);
++      memcpy(eurus_cmd_0x115b->mac_addr, jd->mac_addr, sizeof(jd->mac_addr));
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x115b,
++          eurus_cmd_0x115b, sizeof(*eurus_cmd_0x115b), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 6 */
++
++      h = ps3_eurus_mcast_addr_hash(bcast_addr);
++
++      eurus_cmd_mcast_addr_filter = (struct ps3_eurus_cmd_mcast_addr_filter *) buf;
++      memset(eurus_cmd_mcast_addr_filter, 0, sizeof(*eurus_cmd_mcast_addr_filter));
++      eurus_cmd_mcast_addr_filter->word[PS3_EURUS_MCAST_ADDR_HASH2POS(h)] |=
++          cpu_to_le32(PS3_EURUS_MCAST_ADDR_HASH2VAL(h));
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_SET_MCAST_ADDR_FILTER,
++          eurus_cmd_mcast_addr_filter, sizeof(*eurus_cmd_mcast_addr_filter), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 7 */
++
++      eurus_cmd_0x110d = (struct ps3_eurus_cmd_0x110d *) buf;
++      memset(eurus_cmd_0x110d, 0, sizeof(*eurus_cmd_0x110d));
++      eurus_cmd_0x110d->unknown1 = cpu_to_le32(0xffffffff);
++      eurus_cmd_0x110d->unknown2 = cpu_to_le32(0xffffffff);
++      eurus_cmd_0x110d->unknown3 = cpu_to_le32(0xffffffff);
++      eurus_cmd_0x110d->unknown4 = cpu_to_le32(0xffffffff);
++      eurus_cmd_0x110d->unknown5 = cpu_to_le32(0xffffffff);
++      eurus_cmd_0x110d->unknown6 = cpu_to_le32(0xffffffff);
++      eurus_cmd_0x110d->unknown7 = cpu_to_le32(0xffffffff);
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x110d,
++          eurus_cmd_0x110d, sizeof(*eurus_cmd_0x110d), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 8 */
++
++      eurus_cmd_0x1031 = (struct ps3_eurus_cmd_0x1031 *) buf;
++      memset(eurus_cmd_0x1031, 0, sizeof(*eurus_cmd_0x1031));
++      eurus_cmd_0x1031->unknown1 = 0x0;
++      eurus_cmd_0x1031->unknown2 = 0x0;
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x1031,
++          eurus_cmd_0x1031, sizeof(*eurus_cmd_0x1031), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 9 */
++
++      eurus_cmd_set_mac_addr = (struct ps3_eurus_cmd_set_mac_addr *) buf;
++      memset(eurus_cmd_set_mac_addr, 0, sizeof(*eurus_cmd_set_mac_addr));
++      memcpy(eurus_cmd_set_mac_addr->mac_addr, jd->mac_addr, sizeof(jd->mac_addr));
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_SET_MAC_ADDR,
++          eurus_cmd_set_mac_addr, sizeof(*eurus_cmd_set_mac_addr), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 10 */
++
++      eurus_cmd_set_antenna = (struct ps3_eurus_cmd_set_antenna *) buf;
++      memset(eurus_cmd_set_antenna, 0, sizeof(*eurus_cmd_set_antenna));
++
++      if (((jd->channel_info >> 40) & 0xff) == 0x1) {
++              eurus_cmd_set_antenna->unknown1 = 0x1;
++              eurus_cmd_set_antenna->unknown2 = 0x0;
++      } else if (((jd->channel_info >> 40) & 0xff) == 0x2) {
++              eurus_cmd_set_antenna->unknown1 = 0x1;
++              eurus_cmd_set_antenna->unknown2 = 0x1;
++      } else {
++              eurus_cmd_set_antenna->unknown1 = 0x2;
++              eurus_cmd_set_antenna->unknown2 = 0x2;
++      }
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_SET_ANTENNA,
++          eurus_cmd_set_antenna, sizeof(*eurus_cmd_set_antenna), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 11 */
++
++      eurus_cmd_0x110b = (struct ps3_eurus_cmd_0x110b *) buf;
++      memset(eurus_cmd_0x110b, 0, sizeof(*eurus_cmd_0x110b));
++      eurus_cmd_0x110b->unknown1 = cpu_to_le32(0x1);
++      eurus_cmd_0x110b->unknown2 = cpu_to_le32(0x200000);
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x110b,
++          eurus_cmd_0x110b, sizeof(*eurus_cmd_0x110b), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 12 */
++
++      eurus_cmd_0x1109 = (struct ps3_eurus_cmd_0x1109 *) buf;
++      ps3_eurus_make_cmd_0x1109(eurus_cmd_0x1109, 0x1, 0x0, 0x2715, 0x9, 0x12);
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x1109,
++          eurus_cmd_0x1109, sizeof(*eurus_cmd_0x1109), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 13 */
++
++      eurus_cmd_0x207 = (struct ps3_eurus_cmd_0x207 *) buf;
++      memset(eurus_cmd_0x207, 0, sizeof(*eurus_cmd_0x207));
++      eurus_cmd_0x207->unknown = cpu_to_le32(0x1);
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x207,
++          eurus_cmd_0x207, sizeof(*eurus_cmd_0x207), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 14 */
++
++      eurus_cmd_0x203 = (struct ps3_eurus_cmd_0x203 *) buf;
++      memset(eurus_cmd_0x203, 0, sizeof(*eurus_cmd_0x203));
++      eurus_cmd_0x203->unknown = cpu_to_le32(0x1);
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x203,
++          eurus_cmd_0x203, sizeof(*eurus_cmd_0x203), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* state 15 */
++
++      eurus_cmd_0x105f = (struct ps3_eurus_cmd_0x105f *) buf;
++      memset(eurus_cmd_0x105f, 0, sizeof(*eurus_cmd_0x105f));
++      eurus_cmd_0x105f->channel_info = cpu_to_le16(jd->channel_info >> 48);
++      memcpy(eurus_cmd_0x105f->mac_addr, jd->mac_addr, sizeof(jd->mac_addr));
++
++      if (((jd->channel_info >> 40) & 0xff) == 0x1) {
++              eurus_cmd_0x105f->unknown1 = 0x1;
++              eurus_cmd_0x105f->unknown2 = 0x0;
++      } else if (((jd->channel_info >> 40) & 0xff) == 0x2) {
++              eurus_cmd_0x105f->unknown1 = 0x1;
++              eurus_cmd_0x105f->unknown2 = 0x1;
++      } else {
++              eurus_cmd_0x105f->unknown1 = 0x2;
++              eurus_cmd_0x105f->unknown2 = 0x2;
++      }
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x105f,
++          eurus_cmd_0x105f, sizeof(*eurus_cmd_0x105f), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* device is ready now */
++
++      /* read firmware version */
++
++      eurus_cmd_get_fw_version = (struct ps3_eurus_cmd_get_fw_version *) buf;
++      memset(eurus_cmd_get_fw_version, 0, sizeof(*eurus_cmd_get_fw_version));
++
++      err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_GET_FW_VERSION,
++          eurus_cmd_get_fw_version, sizeof(*eurus_cmd_get_fw_version),
++          &status, &response_length, eurus_cmd_get_fw_version);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      dev_info(&udev->dev, "firmware version: %s\n", (char *) eurus_cmd_get_fw_version->version);
++
++      err = 0;
++
++done:
++
++      kfree(buf);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_probe
++ */
++static int ps3_jupiter_probe(struct usb_interface *interface,
++      const struct usb_device_id *id)
++{
++      struct usb_device *udev = interface_to_usbdev(interface);
++      struct ps3_jupiter_dev *jd;
++      u64 v1, v2;
++      int err;
++
++      if (ps3jd) {
++              dev_err(&udev->dev, "only one device is supported\n");
++              return -EBUSY;
++      }
++
++      ps3jd = jd = kzalloc(sizeof(struct ps3_jupiter_dev), GFP_KERNEL);
++      if (!jd)
++              return -ENOMEM;
++
++      jd->udev = usb_get_dev(udev);
++      usb_set_intfdata(interface, jd);
++
++      spin_lock_init(&jd->cmd_lock);
++
++      BLOCKING_INIT_NOTIFIER_HEAD(&jd->event_listeners);
++      INIT_LIST_HEAD(&jd->event_list);
++      spin_lock_init(&jd->event_list_lock);
++
++      init_completion(&jd->event_comp);
++
++      jd->event_listener.notifier_call = ps3_jupiter_event_handler;
++
++      err = _ps3_jupiter_register_event_listener(jd, &jd->event_listener);
++      if (err) {
++              dev_err(&udev->dev, "could not register event listener (%d)\n", err);
++              goto fail;
++      }
++
++      err = ps3_jupiter_create_event_worker(jd);
++      if (err) {
++              dev_err(&udev->dev, "could not create event work queue (%d)\n", err);
++              goto fail;
++      }
++
++      err = ps3_jupiter_alloc_urbs(jd);
++      if (err) {
++              dev_err(&udev->dev, "could not allocate URBs (%d)\n", err);
++              goto fail;
++      }
++
++      err = usb_submit_urb(jd->irq_urb, GFP_KERNEL);
++      if (err) {
++              dev_err(&udev->dev, "could not submit IRQ URB (%d)\n", err);
++              goto fail;
++      }
++
++      err = ps3_jupiter_dev_auth(jd);
++      if (err) {
++              dev_err(&udev->dev, "could not authenticate device (%d)\n", err);
++              goto fail;
++      }
++
++      /* get MAC address */
++
++      err = lv1_net_control(LV1_SB_BUS_ID, LV1_GELIC_DEV_ID,
++          LV1_GET_MAC_ADDRESS, 0, 0, 0, &v1, &v2);
++      if (err) {
++              dev_err(&udev->dev, "could not get MAC address (%d)\n", err);
++              err = -ENODEV;
++              goto fail;
++      }
++
++      v1 <<= 16;
++
++      if (!is_valid_ether_addr((unsigned char *) &v1)) {
++              dev_err(&udev->dev, "got invalid MAC address\n");
++              err = -ENODEV;
++              goto fail;
++      }
++
++      memcpy(jd->mac_addr, &v1, ETH_ALEN);
++
++      dev_info(&udev->dev, "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
++          jd->mac_addr[0], jd->mac_addr[1], jd->mac_addr[2],
++          jd->mac_addr[3], jd->mac_addr[4], jd->mac_addr[5]);
++
++      /* get channel info */
++
++      err = lv1_net_control(LV1_SB_BUS_ID, LV1_GELIC_DEV_ID,
++          LV1_GET_CHANNEL_INFO, 0, 0, 0, &v1, &v2);
++      if (err) {
++              /* don't quit here and try to recover later */
++              dev_err(&udev->dev, "could not get channel info (%d)\n", err);
++      }
++
++      if (err)
++              jd->channel_info = (0x7ffull << 48);
++      else
++              jd->channel_info = v1;
++
++      dev_info(&udev->dev, "channel info: %016llx\n", jd->channel_info);
++
++      err = ps3_jupiter_dev_init(jd);
++      if (err) {
++              dev_err(&udev->dev, "could not initialize device (%d)\n", err);
++              goto fail;
++      }
++
++      jd->dev_ready = 1;
++
++      return 0;
++
++fail:
++
++      ps3_jupiter_free_urbs(jd);
++
++      ps3_jupiter_destroy_event_worker(jd);
++
++      ps3_jupiter_free_event_list(jd);
++
++      usb_set_intfdata(interface, NULL);
++      usb_put_dev(udev);
++
++      kfree(jd);
++      ps3jd = NULL;
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_disconnect
++ */
++static void ps3_jupiter_disconnect(struct usb_interface *interface)
++{
++      struct ps3_jupiter_dev *jd = usb_get_intfdata(interface);
++      struct usb_device *udev = jd->udev;
++
++      jd->dev_ready = 0;
++
++      ps3_jupiter_free_urbs(jd);
++
++      ps3_jupiter_destroy_event_worker(jd);
++
++      ps3_jupiter_free_event_list(jd);
++
++      usb_set_intfdata(interface, NULL);
++      usb_put_dev(udev);
++
++      kfree(jd);
++      ps3jd = NULL;
++}
++
++#ifdef CONFIG_PM
++/*
++ * ps3_jupiter_suspend
++ */
++static int ps3_jupiter_suspend(struct usb_interface *interface, pm_message_t state)
++{
++      /*XXX: implement */
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_resume
++ */
++static int ps3_jupiter_resume(struct usb_interface *interface)
++{
++      /*XXX: implement */
++
++      return 0;
++}
++#endif /* CONFIG_PM */
++
++static struct usb_device_id ps3_jupiter_devtab[] = {
++      {
++              .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
++              .idVendor = 0x054c,
++              .idProduct = 0x036f,
++              .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
++              .bInterfaceSubClass = 2,
++              .bInterfaceProtocol = 1
++      },
++      { }
++};
++
++static struct usb_driver ps3_jupiter_drv = {
++      .name           = KBUILD_MODNAME,
++      .id_table       = ps3_jupiter_devtab,
++      .probe          = ps3_jupiter_probe,
++      .disconnect     = ps3_jupiter_disconnect,
++#ifdef CONFIG_PM
++      .suspend        = ps3_jupiter_suspend,
++      .resume         = ps3_jupiter_resume,
++#endif /* CONFIG_PM */
++};
++
++/*
++ * ps3_jupiter_init
++ */
++static int __init ps3_jupiter_init(void)
++{
++      return usb_register(&ps3_jupiter_drv);
++}
++
++/*
++ * ps3_jupiter_exit
++ */
++static void __exit ps3_jupiter_exit(void)
++{
++      usb_deregister(&ps3_jupiter_drv);
++}
++
++module_init(ps3_jupiter_init);
++module_exit(ps3_jupiter_exit);
++
++MODULE_SUPPORTED_DEVICE("PS3 Jupiter");
++MODULE_DEVICE_TABLE(usb, ps3_jupiter_devtab);
++MODULE_DESCRIPTION("PS3 Jupiter");
++MODULE_AUTHOR("glevand");
++MODULE_LICENSE("GPL");
+--- /dev/null  2012-11-01 00:09:58.397610712 -0800
++++ b/drivers/net/wireless/ps3jupiter/ps3_jupiter_sta.c        2012-11-01 03:22:36.000000000 -0800
+@@ -0,0 +1,2934 @@
++
++/*
++ * PS3 Jupiter STA
++ *
++ * Copyright (C) 2011, 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++#include <linux/interrupt.h>
++#include <linux/notifier.h>
++
++#include <linux/etherdevice.h>
++#include <linux/ethtool.h>
++#include <linux/if_arp.h>
++#include <linux/ieee80211.h>
++#include <net/iw_handler.h>
++
++#include "ps3_eurus.h"
++#include "ps3_jupiter.h"
++
++#define PS3_JUPITER_STA_CMD_BUFSIZE           2048
++
++#define PS3_JUPITER_STA_IFACE                 0x4
++#define PS3_JUPITER_STA_EP                    0x6
++
++#define PS3_JUPITER_STA_WEP_KEYS              4
++
++#define PS3_JUPITER_STA_WPA_PSK_LENGTH                32
++
++#define PS3_JUPITER_STA_RX_URBS                       4
++#define PS3_JUPITER_STA_RX_BUFSIZE            0x620
++
++#define PS3_JUPITER_STA_TX_URBS                       16
++
++#define PS3_JUPITER_STA_CHANNEL_DWELL         0x64
++
++#define PS3_JUPITER_STA_SCAN_VALID_TIME_SEC   60
++
++enum ps3_jupiter_sta_status {
++      PS3_JUPITER_STA_READY = 0,
++};
++
++enum ps3_jupiter_sta_scan_status {
++      PS3_JUPITER_STA_SCAN_INVALID = 0,
++      PS3_JUPITER_STA_SCAN_IN_PROGRESS,
++      PS3_JUPITER_STA_SCAN_OK
++};
++
++enum ps3_jupiter_sta_assoc_status {
++      PS3_JUPITER_STA_ASSOC_INVALID = 0,
++      PS3_JUPITER_STA_ASSOC_IN_PROGRESS,
++      PS3_JUPITER_STA_ASSOC_OK
++};
++
++enum ps3_jupiter_sta_config_bits {
++      PS3_JUPITER_STA_CONFIG_ESSID_SET = 0,
++      PS3_JUPITER_STA_CONFIG_BSSID_SET,
++      PS3_JUPITER_STA_CONFIG_CHANNEL_SET,
++      PS3_JUPITER_STA_CONFIG_WPA_PSK_SET
++};
++
++enum ps3_jupiter_sta_bss_type {
++      PS3_JUPITER_STA_BSS_TYPE_INFRA = 0,
++      PS3_JUPITER_STA_BSS_TYPE_ADHOC,
++};
++
++enum ps3_jupiter_sta_opmode {
++      PS3_JUPITER_STA_OPMODE_11B = 0,
++      PS3_JUPITER_STA_OPMODE_11G,
++      PS3_JUPITER_STA_OPMODE_11BG
++};
++
++enum ps3_jupiter_sta_auth_mode {
++      PS3_JUPITER_STA_AUTH_OPEN = 0,
++      PS3_JUPITER_STA_AUTH_SHARED_KEY
++};
++
++enum ps3_jupiter_sta_wpa_mode {
++      PS3_JUPITER_STA_WPA_MODE_NONE = 0,
++      PS3_JUPITER_STA_WPA_MODE_WPA,
++      PS3_JUPITER_STA_WPA_MODE_WPA2
++};
++
++enum ps3_jupiter_sta_cipher_mode {
++      PS3_JUPITER_STA_CIPHER_NONE = 0,
++      PS3_JUPITER_STA_CIPHER_WEP,
++      PS3_JUPITER_STA_CIPHER_TKIP,
++      PS3_JUPITER_STA_CIPHER_AES
++};
++
++struct ps3_jupiter_sta_scan_result {
++      struct list_head list;
++
++      u8 bssid[6];
++      u16 capability;
++      u8 rssi;
++
++      u8 *essid_ie;
++      u8 *ds_param_set_ie;
++      u8 *supp_rates_ie;
++      u8 *ext_supp_rates_ie;
++      u8 *rsn_ie;
++      u8 *wpa_ie;
++
++      unsigned int ie_length;
++      u8 ie[0];
++};
++
++struct ps3_jupiter_sta_dev {
++      struct net_device *netdev;
++
++      struct usb_device *udev;
++
++      spinlock_t lock;
++
++      unsigned long status;
++
++      struct iw_public_data wireless_data;
++      struct iw_statistics wireless_stat;
++
++      struct notifier_block event_listener;
++
++      u16 channel_info;
++
++      struct mutex scan_lock;
++      struct list_head scan_result_list;
++      enum ps3_jupiter_sta_scan_status scan_status;
++      struct completion scan_done_comp;
++      unsigned long scan_expires;
++
++      unsigned long config_status;
++
++      enum ps3_jupiter_sta_bss_type bss_type;
++
++      enum ps3_jupiter_sta_opmode opmode;
++
++      enum ps3_jupiter_sta_auth_mode auth_mode;
++
++      enum ps3_jupiter_sta_wpa_mode wpa_mode;
++      enum ps3_jupiter_sta_cipher_mode group_cipher_mode;
++      enum ps3_jupiter_sta_cipher_mode pairwise_cipher_mode;
++
++      u8 essid[IW_ESSID_MAX_SIZE];
++      unsigned int essid_length;
++
++      u8 desired_bssid[ETH_ALEN];
++      u8 bssid[ETH_ALEN];
++
++      u8 channel;
++
++      unsigned long key_config_status;
++      u8 key[PS3_JUPITER_STA_WEP_KEYS][IW_ENCODING_TOKEN_MAX];
++      unsigned int key_length[PS3_JUPITER_STA_WEP_KEYS];
++      unsigned int curr_key_index;
++
++      u8 psk[PS3_JUPITER_STA_WPA_PSK_LENGTH];
++
++      struct mutex assoc_lock;
++      struct workqueue_struct *assoc_queue;
++      struct delayed_work assoc_work;
++      enum ps3_jupiter_sta_assoc_status assoc_status;
++      struct completion assoc_done_comp;
++
++      struct usb_anchor rx_urb_anchor;
++      struct sk_buff_head rx_skb_queue;
++      struct tasklet_struct rx_tasklet;
++
++      struct usb_anchor tx_urb_anchor;
++      atomic_t tx_submitted_urbs;
++};
++
++static int max_txurbs = PS3_JUPITER_STA_TX_URBS;
++module_param(max_txurbs, int, S_IRUGO);
++MODULE_PARM_DESC(max_txurbs, "Maximum number of Tx URBs");
++
++static const int ps3_jupiter_sta_channel_freq[] = {
++      2412,
++      2417,
++      2422,
++      2427,
++      2432,
++      2437,
++      2442,
++      2447,
++      2452,
++      2457,
++      2462,
++      2467,
++      2472,
++      2484
++};
++
++static const int ps3_jupiter_sta_bitrate[] = {
++      1000000,
++      2000000,
++      5500000,
++      11000000,
++      6000000,
++      9000000,
++      12000000,
++      18000000,
++      24000000,
++      36000000,
++      48000000,
++      54000000
++};
++
++static void ps3_jupiter_sta_free_scan_results(struct ps3_jupiter_sta_dev *jstad);
++
++static int ps3_jupiter_sta_start_scan(struct ps3_jupiter_sta_dev *jstad,
++      u8 *essid, size_t essid_length, u16 channels, u8 active, u16 channel_dwell);
++
++static char *ps3_jupiter_sta_translate_scan_result(struct ps3_jupiter_sta_dev *jstad,
++      struct ps3_jupiter_sta_scan_result *scan_result,
++      struct iw_request_info *info, char *stream, char *ends);
++
++static void ps3_jupiter_sta_start_assoc(struct ps3_jupiter_sta_dev *jstad);
++
++static int ps3_jupiter_sta_disassoc(struct ps3_jupiter_sta_dev *jstad);
++
++static void ps3_jupiter_sta_reset_state(struct ps3_jupiter_sta_dev *jstad);
++
++static int ps3_jupiter_sta_prepare_rx_urb(struct ps3_jupiter_sta_dev *jstad,
++      struct urb *urb);
++
++static void ps3_jupiter_sta_purge_rx_skb_queue(struct ps3_jupiter_sta_dev *jstad);
++
++static void ps3_jupiter_sta_free_tx_urbs(struct ps3_jupiter_sta_dev *jstad);
++
++static int ps3_jupiter_sta_tx_skb(struct ps3_jupiter_sta_dev *jstad, struct sk_buff *skb);
++
++/*
++ * ps3_jupiter_sta_freq_to_channel
++ */
++static u8 ps3_jupiter_sta_freq_to_channel(u32 freq)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(ps3_jupiter_sta_channel_freq); i++) {
++              if (ps3_jupiter_sta_channel_freq[i] == freq)
++                      return (i + 1);
++      }
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_send_iw_ap_event
++ */
++static void ps3_jupiter_sta_send_iw_ap_event(struct ps3_jupiter_sta_dev *jstad, u8 *bssid)
++{
++      union iwreq_data iwrd;
++
++      memset(&iwrd, 0, sizeof(iwrd));
++
++      if (bssid)
++              memcpy(iwrd.ap_addr.sa_data, bssid, ETH_ALEN);
++
++      iwrd.ap_addr.sa_family = ARPHRD_ETHER;
++
++      wireless_send_event(jstad->netdev, SIOCGIWAP, &iwrd, NULL);
++}
++
++/*
++ * ps3_jupiter_sta_get_name
++ */
++static int ps3_jupiter_sta_get_name(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      strcpy(wrqu->name, "IEEE 802.11bg");
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_get_nick
++ */
++static int ps3_jupiter_sta_get_nick(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      strcpy(extra, "ps3_jupiter_sta");
++      wrqu->data.length = strlen(extra);
++      wrqu->data.flags = 1;
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_get_range
++ */
++static int ps3_jupiter_sta_get_range(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_point *point = &wrqu->data;
++      struct iw_range *range = (struct iw_range *) extra;
++      unsigned int i, chan;
++
++      point->length = sizeof(struct iw_range);
++      memset(range, 0, sizeof(struct iw_range));
++
++      range->we_version_compiled = WIRELESS_EXT;
++      range->we_version_source = 22;
++
++      for (i = 0, chan = 0;
++           (i < ARRAY_SIZE(ps3_jupiter_sta_channel_freq)) && (chan < IW_MAX_FREQUENCIES); i++) {
++              if (jstad->channel_info & (1 << i)) {
++                      range->freq[chan].i = i + 1;
++                      range->freq[chan].m = ps3_jupiter_sta_channel_freq[i];
++                      range->freq[chan].e = 6;
++                      chan++;
++              }
++      }
++
++      range->num_frequency = chan;
++      range->old_num_frequency = chan;
++      range->num_channels = chan;
++      range->old_num_channels = chan;
++
++      for (i = 0; i < ARRAY_SIZE(ps3_jupiter_sta_bitrate); i++)
++              range->bitrate[i] = ps3_jupiter_sta_bitrate[i];
++      range->num_bitrates = i;
++
++      range->max_qual.qual = 100;
++      range->max_qual.level = 100;
++      range->avg_qual.qual = 50;
++      range->avg_qual.level = 50;
++      range->sensitivity = 0;
++
++      IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
++      IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
++      IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
++
++      range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
++          IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP |
++          IW_ENC_CAPA_4WAY_HANDSHAKE;
++
++      range->encoding_size[0] = 5;
++      range->encoding_size[1] = 13;
++      range->encoding_size[2] = 32;
++      range->num_encoding_sizes = 3;
++      range->max_encoding_tokens = 4;
++
++      range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_CHANNEL;
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_set_mode
++ */
++static int ps3_jupiter_sta_set_mode(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++
++      switch (wrqu->mode) {
++      case IW_MODE_INFRA:
++              jstad->bss_type = PS3_JUPITER_STA_BSS_TYPE_INFRA;
++      break;
++      case IW_MODE_ADHOC:
++              jstad->bss_type = PS3_JUPITER_STA_BSS_TYPE_ADHOC;
++      break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_get_mode
++ */
++static int ps3_jupiter_sta_get_mode(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      wrqu->mode = IW_MODE_INFRA;
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_set_freq
++ */
++static int ps3_jupiter_sta_set_freq(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_freq *freq = &wrqu->freq;
++      __u8 channel;
++      unsigned long irq_flags;
++      int err;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (!freq->m) {
++              jstad->channel = 0;
++              clear_bit(PS3_JUPITER_STA_CONFIG_CHANNEL_SET, &jstad->config_status);
++      } else {
++              if (freq->e == 1)
++                      channel = ps3_jupiter_sta_freq_to_channel(freq->m / 100000);
++              else
++                      channel = freq->m;
++
++              if (!channel || !(jstad->channel_info & (1 << (channel - 1)))) {
++                      err = -EINVAL;
++                      goto done;
++              }
++
++              jstad->channel = channel;
++              set_bit(PS3_JUPITER_STA_CONFIG_CHANNEL_SET, &jstad->config_status);
++      }
++
++      err = 0;
++
++done:
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_freq
++ */
++static int ps3_jupiter_sta_get_freq(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_freq *freq = &wrqu->freq;
++      unsigned long irq_flags;
++
++      pr_debug("%s: called\n", __func__);
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (test_bit(PS3_JUPITER_STA_CONFIG_CHANNEL_SET, &jstad->config_status)) {
++              freq->e = 1;
++              freq->m = ps3_jupiter_sta_channel_freq[jstad->channel - 1] * 100000;
++      } else {
++              freq->e = 0;
++              freq->m = 0;
++      }
++
++      pr_debug("%s: done\n", __func__);
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_set_scan
++ */
++static int ps3_jupiter_sta_set_scan(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_scan_req *scan_req;
++      u8 *essid = NULL;
++      size_t essid_length = 0;
++      u16 channels = jstad->channel_info;
++      __u8 channel;
++      u8 active = 1;
++      u16 channel_dwell = PS3_JUPITER_STA_CHANNEL_DWELL;
++      int i;
++      int err;
++
++      pr_debug("%s: called\n", __func__);
++
++      if (wrqu->data.length == sizeof(*scan_req)) {
++              scan_req = (struct iw_scan_req *) extra;
++
++              if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
++                      essid = scan_req->essid;
++                      essid_length = scan_req->essid_len;
++
++                      pr_debug("%s: essid %s\n", __func__, essid);
++              }
++
++              if (scan_req->num_channels > 0)
++                      channels = 0;
++
++              for (i = 0; i < scan_req->num_channels; i++) {
++                      if (scan_req->channel_list[i].e == 1)
++                              channel = ps3_jupiter_sta_freq_to_channel(scan_req->channel_list[i].m / 100000);
++                      else
++                              channel = scan_req->channel_list[i].m;
++
++                      channels |= 1 << (channel - 1);
++              }
++
++              pr_debug("%s: channels 0x%04x\n", __func__, channels);
++
++              active = (scan_req->scan_type == IW_SCAN_TYPE_ACTIVE);
++      }
++
++      err = ps3_jupiter_sta_start_scan(jstad, essid, essid_length, channels,
++          active, channel_dwell);
++
++      pr_debug("%s: done\n", __func__);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_scan
++ */
++static int ps3_jupiter_sta_get_scan(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct ps3_jupiter_sta_scan_result *scan_result;
++      char *stream = extra;
++      char *ends = stream + wrqu->data.length;
++      int err;
++
++      if (mutex_lock_interruptible(&jstad->scan_lock))
++              return -EAGAIN;
++
++      if (jstad->scan_status == PS3_JUPITER_STA_SCAN_IN_PROGRESS) {
++              err = -EAGAIN;
++              goto done;
++      } else if (jstad->scan_status == PS3_JUPITER_STA_SCAN_INVALID) {
++              err = -ENODEV;
++              goto done;
++      }
++
++      /* translate scan results */
++
++      list_for_each_entry(scan_result, &jstad->scan_result_list, list) {
++              stream = ps3_jupiter_sta_translate_scan_result(jstad, scan_result,
++                  info, stream, ends);
++
++              if ((ends - stream) <= IW_EV_ADDR_LEN) {
++                      err = -E2BIG;
++                      goto done;
++              }
++      }
++
++      wrqu->data.length = stream - extra;
++      wrqu->data.flags = 0;
++
++      err = 0;
++
++done:
++
++      mutex_unlock(&jstad->scan_lock);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_set_auth
++ */
++static int ps3_jupiter_sta_set_auth(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_param *param = &wrqu->param;
++      unsigned long irq_flags;
++      int err = 0;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      switch (param->flags & IW_AUTH_INDEX) {
++      case IW_AUTH_WPA_VERSION:
++              if (param->value & IW_AUTH_WPA_VERSION_DISABLED) {
++                      jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE;
++                      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP;
++                      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP;
++              } else if (param->value & IW_AUTH_WPA_VERSION_WPA) {
++                      jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_WPA;
++                      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP;
++                      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP;
++              } else if (param->value & IW_AUTH_WPA_VERSION_WPA2) {
++                      jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_WPA2;
++                      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_AES;
++                      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_AES;
++              }
++      break;
++      case IW_AUTH_CIPHER_GROUP:
++              if (param->value & IW_AUTH_CIPHER_NONE)
++                      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++              else if (param->value & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
++                      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP;
++              else if (param->value & IW_AUTH_CIPHER_TKIP)
++                      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP;
++              else if (param->value & IW_AUTH_CIPHER_CCMP)
++                      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_AES;
++      break;
++      case IW_AUTH_CIPHER_PAIRWISE:
++              if (param->value & IW_AUTH_CIPHER_NONE)
++                      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++              else if (param->value & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
++                      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP;
++              else if (param->value & IW_AUTH_CIPHER_TKIP)
++                      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP;
++              else if (param->value & IW_AUTH_CIPHER_CCMP)
++                      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_AES;
++      break;
++      case IW_AUTH_80211_AUTH_ALG:
++              if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
++                      jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN;
++              else if (param->value & IW_AUTH_ALG_SHARED_KEY)
++                      jstad->auth_mode = PS3_JUPITER_STA_AUTH_SHARED_KEY;
++              else
++                      err = -EINVAL;
++      break;
++      case IW_AUTH_WPA_ENABLED:
++              if (param->value)
++                      jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_WPA;
++              else
++                      jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE;
++      break;
++      case IW_AUTH_KEY_MGMT:
++              if (!(param->value & IW_AUTH_KEY_MGMT_PSK))
++                      err = -EOPNOTSUPP;
++      break;
++      default:
++              err = -EOPNOTSUPP;
++      }
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_auth
++ */
++static int ps3_jupiter_sta_get_auth(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_param *param = &wrqu->param;
++      unsigned long irq_flags;
++      int err = 0;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      switch (param->flags & IW_AUTH_INDEX) {
++      case IW_AUTH_WPA_VERSION:
++      switch (jstad->wpa_mode) {
++      case PS3_JUPITER_STA_WPA_MODE_WPA:
++              param->value |= IW_AUTH_WPA_VERSION_WPA;
++      break;
++      case PS3_JUPITER_STA_WPA_MODE_WPA2:
++              param->value |= IW_AUTH_WPA_VERSION_WPA2;
++      break;
++      default:
++              param->value |= IW_AUTH_WPA_VERSION_DISABLED;
++      }
++      break;
++      case IW_AUTH_80211_AUTH_ALG:
++      switch (jstad->auth_mode) {
++      case PS3_JUPITER_STA_AUTH_OPEN:
++              param->value |= IW_AUTH_ALG_OPEN_SYSTEM;
++      break;
++      case PS3_JUPITER_STA_AUTH_SHARED_KEY:
++              param->value |= IW_AUTH_ALG_SHARED_KEY;
++      break;
++      }
++      break;
++      case IW_AUTH_WPA_ENABLED:
++      switch (jstad->wpa_mode) {
++      case PS3_JUPITER_STA_WPA_MODE_WPA:
++      case PS3_JUPITER_STA_WPA_MODE_WPA2:
++              param->value = 1;
++      break;
++      default:
++              param->value = 0;
++      }
++      break;
++      default:
++              err = -EOPNOTSUPP;
++      }
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_set_essid
++ */
++static int ps3_jupiter_sta_set_essid(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      unsigned long irq_flags;
++
++      pr_debug("%s: called\n", __func__);
++
++      if (wrqu->essid.length > IW_ESSID_MAX_SIZE)
++              return -EINVAL;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (wrqu->essid.flags) {
++              memcpy(jstad->essid, extra, wrqu->essid.length);
++              jstad->essid_length = wrqu->essid.length;
++              set_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status);
++
++              pr_debug("%s: essid %s\n", __func__, extra);
++      } else {
++              clear_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status);
++
++              pr_debug("%s: essid any\n", __func__);
++      }
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      ps3_jupiter_sta_start_assoc(jstad);
++
++      pr_debug("%s: done\n", __func__);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_get_essid
++ */
++static int ps3_jupiter_sta_get_essid(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      unsigned long irq_flags;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (test_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status)) {
++              memcpy(extra, jstad->essid, jstad->essid_length);
++              wrqu->essid.length = jstad->essid_length;
++              wrqu->essid.flags = 1;
++      } else {
++              wrqu->essid.flags = 0;
++      }
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_set_ap
++ */
++static int ps3_jupiter_sta_set_ap(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      unsigned long irq_flags;
++
++      if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
++              return -EINVAL;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (is_valid_ether_addr(wrqu->ap_addr.sa_data)) {
++              memcpy(jstad->desired_bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
++              set_bit(PS3_JUPITER_STA_CONFIG_BSSID_SET, &jstad->config_status);
++      } else {
++              memset(jstad->desired_bssid, 0, ETH_ALEN);
++      }
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_get_ap
++ */
++static int ps3_jupiter_sta_get_ap(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      unsigned long irq_flags;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      wrqu->ap_addr.sa_family = ARPHRD_ETHER;
++      memcpy(wrqu->ap_addr.sa_data, jstad->desired_bssid, ETH_ALEN);
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_set_encode
++ */
++static int ps3_jupiter_sta_set_encode(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_point *enc = &wrqu->encoding;
++      __u16 flags = enc->flags & IW_ENCODE_FLAGS;
++      int key_index = enc->flags & IW_ENCODE_INDEX;
++      int key_index_provided;
++      unsigned long irq_flags;
++      int err = 0;
++
++      if (key_index > PS3_JUPITER_STA_WEP_KEYS)
++              return -EINVAL;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (key_index) {
++              key_index--;
++              key_index_provided = 1;
++      } else {
++              key_index = jstad->curr_key_index;
++              key_index_provided = 0;
++      }
++
++      if (flags & IW_ENCODE_NOKEY) {
++              if (!(flags & ~IW_ENCODE_NOKEY) && key_index_provided) {
++                      jstad->curr_key_index = key_index;
++                      goto done;
++              }
++
++              if (flags & IW_ENCODE_DISABLED) {
++                      if (key_index_provided) {
++                              clear_bit(key_index, &jstad->key_config_status);
++                      } else {
++                              jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++                              jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++                              jstad->key_config_status = 0;
++                      }
++              }
++
++              if (flags & IW_ENCODE_OPEN)
++                      jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN;
++              else if (flags & IW_ENCODE_RESTRICTED)
++                      jstad->auth_mode = PS3_JUPITER_STA_AUTH_SHARED_KEY;
++      } else {
++              if (enc->length > IW_ENCODING_TOKEN_MAX) {
++                      err = -EINVAL;
++                      goto done;
++              }
++
++              memcpy(jstad->key[key_index], extra, enc->length);
++              jstad->key_length[key_index] = enc->length;
++              set_bit(key_index, &jstad->key_config_status);
++
++              jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP;
++              jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP;
++      }
++
++done:
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_encode
++ */
++static int ps3_jupiter_sta_get_encode(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_point *enc = &wrqu->encoding;
++      int key_index = enc->flags & IW_ENCODE_INDEX;
++      int key_index_provided;
++      unsigned long irq_flags;
++      int err = 0;
++
++      if (key_index > PS3_JUPITER_STA_WEP_KEYS)
++              return -EINVAL;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (key_index) {
++              key_index--;
++              key_index_provided = 1;
++      } else {
++              key_index = jstad->curr_key_index;
++              key_index_provided = 0;
++      }
++
++      if (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_WEP) {
++              switch (jstad->auth_mode) {
++              case PS3_JUPITER_STA_AUTH_OPEN:
++                      enc->flags |= IW_ENCODE_OPEN;
++                      break;
++              case PS3_JUPITER_STA_AUTH_SHARED_KEY:
++                      enc->flags |= IW_ENCODE_RESTRICTED;
++                      break;
++              }
++      } else {
++              enc->flags = IW_ENCODE_DISABLED;
++      }
++
++      if (test_bit(key_index, &jstad->key_config_status)) {
++              if (enc->length < jstad->key_length[key_index]) {
++                      err = -EINVAL;
++                      goto done;
++              }
++
++              memcpy(extra, jstad->key[key_index], jstad->key_length[key_index]);
++              enc->length = jstad->key_length[key_index];
++      } else {
++              enc->length = 0;
++              enc->flags |= IW_ENCODE_NOKEY;
++      }
++
++      enc->flags |= (key_index + 1);
++
++done:
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_set_encodeext
++ */
++static int ps3_jupiter_sta_set_encodeext(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_point *enc = &wrqu->encoding;
++      struct iw_encode_ext *enc_ext = (struct iw_encode_ext *) extra;
++      __u16 flags = enc->flags & IW_ENCODE_FLAGS;
++      int key_index = enc->flags & IW_ENCODE_INDEX;
++      unsigned long irq_flags;
++      int err = 0;
++
++      if (key_index > PS3_JUPITER_STA_WEP_KEYS)
++              return -EINVAL;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (key_index)
++              key_index--;
++      else
++              key_index = jstad->curr_key_index;
++
++      if (!enc->length && (enc_ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
++              jstad->curr_key_index = key_index;
++      } else if ((enc_ext->alg == IW_ENCODE_ALG_NONE) || (flags & IW_ENCODE_DISABLED)) {
++              jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN;
++              jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE;
++              jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++              jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++      } else if (enc_ext->alg == IW_ENCODE_ALG_WEP) {
++              if (flags & IW_ENCODE_OPEN)
++                      jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN;
++              else if (flags & IW_ENCODE_RESTRICTED)
++                      jstad->auth_mode = PS3_JUPITER_STA_AUTH_SHARED_KEY;
++
++              if (enc_ext->key_len > IW_ENCODING_TOKEN_MAX) {
++                      err = -EINVAL;
++                      goto done;
++              }
++
++              memcpy(jstad->key[key_index], enc_ext->key, enc_ext->key_len);
++              jstad->key_length[key_index] = enc_ext->key_len;
++              set_bit(key_index, &jstad->key_config_status);
++      } else if (enc_ext->alg == IW_ENCODE_ALG_PMK) {
++              if (enc_ext->key_len != PS3_JUPITER_STA_WPA_PSK_LENGTH) {
++                      err = -EINVAL;
++                      goto done;
++              }
++
++              memcpy(jstad->psk, enc_ext->key, enc_ext->key_len);
++              set_bit(PS3_JUPITER_STA_CONFIG_WPA_PSK_SET, &jstad->config_status);
++      }
++
++done:
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_encodeext
++ */
++static int ps3_jupiter_sta_get_encodeext(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_point *enc = &wrqu->encoding;
++      struct iw_encode_ext *enc_ext = (struct iw_encode_ext *) extra;
++      int key_index = enc->flags & IW_ENCODE_INDEX;
++      unsigned long irq_flags;
++      int err = 0;
++
++      if ((enc->length - sizeof(struct iw_encode_ext)) < 0)
++              return -EINVAL;
++
++      if (key_index > PS3_JUPITER_STA_WEP_KEYS)
++              return -EINVAL;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (key_index)
++              key_index--;
++      else
++              key_index = jstad->curr_key_index;
++
++      memset(enc_ext, 0, sizeof(*enc_ext));
++
++      switch (jstad->group_cipher_mode) {
++      case PS3_JUPITER_STA_CIPHER_WEP:
++              enc_ext->alg = IW_ENCODE_ALG_WEP;
++              enc->flags |= IW_ENCODE_ENABLED;
++      break;
++      case PS3_JUPITER_STA_CIPHER_TKIP:
++              enc_ext->alg = IW_ENCODE_ALG_TKIP;
++              enc->flags |= IW_ENCODE_ENABLED;
++      break;
++      case PS3_JUPITER_STA_CIPHER_AES:
++              enc_ext->alg = IW_ENCODE_ALG_CCMP;
++              enc->flags |= IW_ENCODE_ENABLED;
++      break;
++      default:
++              enc_ext->alg = IW_ENCODE_ALG_NONE;
++              enc->flags |= IW_ENCODE_NOKEY;
++      }
++
++      if (!(enc->flags & IW_ENCODE_NOKEY)) {
++              if ((enc->length - sizeof(struct iw_encode_ext)) < jstad->key_length[key_index]) {
++                      err = -E2BIG;
++                      goto done;
++              }
++
++              if (test_bit(key_index, &jstad->key_config_status))
++                      memcpy(enc_ext->key, jstad->key[key_index], jstad->key_length[key_index]);
++      }
++
++done:
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_set_genie
++ */
++static int ps3_jupiter_sta_set_genie(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      /* XXX: implement */
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_get_genie
++ */
++static int ps3_jupiter_sta_get_genie(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      /* XXX: implement */
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_set_mlme
++ */
++static int ps3_jupiter_sta_set_mlme(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      struct iw_mlme *mlme = (struct iw_mlme *) extra;
++
++      switch (mlme->cmd) {
++      case IW_MLME_DEAUTH:
++      break;
++      case IW_MLME_DISASSOC:
++              ps3_jupiter_sta_disassoc(jstad);
++      break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      return 0;
++}
++
++#ifdef CONFIG_WEXT_PRIV
++/*
++ * ps3_jupiter_sta_set_opmode
++ */
++static int ps3_jupiter_sta_set_opmode(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      int opmode = *(int *) extra;
++      unsigned long irq_flags;
++      int err = 0;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      switch (opmode) {
++      case PS3_JUPITER_STA_OPMODE_11B:
++      case PS3_JUPITER_STA_OPMODE_11G:
++      case PS3_JUPITER_STA_OPMODE_11BG:
++              jstad->opmode = opmode;
++      break;
++      default:
++              err = -EINVAL;
++      }
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_opmode
++ */
++static int ps3_jupiter_sta_get_opmode(struct net_device *netdev,
++      struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++      unsigned long irq_flags;
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      memcpy(extra, &jstad->opmode, sizeof(jstad->opmode));
++      wrqu->data.length = sizeof(jstad->opmode);
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      return 0;
++}
++#endif
++
++/*
++ * ps3_jupiter_sta_get_wireless_stats
++ */
++static struct iw_statistics *ps3_jupiter_sta_get_wireless_stats(struct net_device *netdev)
++{
++      /* XXX: implement */
++
++      return NULL;
++}
++
++/*
++ * ps3_jupiter_sta_open
++ */
++static int ps3_jupiter_sta_open(struct net_device *netdev)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++
++      pr_debug("%s: called\n", __func__);
++
++      usb_unpoison_anchored_urbs(&jstad->tx_urb_anchor);
++
++      ps3_jupiter_sta_start_assoc(jstad);
++
++      netif_start_queue(netdev);
++
++      pr_debug("%s: done\n", __func__);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_stop
++ */
++static int ps3_jupiter_sta_stop(struct net_device *netdev)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++
++      pr_debug("%s: called\n", __func__);
++
++      netif_stop_queue(netdev);
++
++      cancel_delayed_work(&jstad->assoc_work);
++
++      if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK)
++              ps3_jupiter_sta_disassoc(jstad);
++
++      tasklet_kill(&jstad->rx_tasklet);
++      ps3_jupiter_sta_purge_rx_skb_queue(jstad);
++
++      ps3_jupiter_sta_free_tx_urbs(jstad);
++
++      ps3_jupiter_sta_free_scan_results(jstad);
++
++      ps3_jupiter_sta_reset_state(jstad);
++
++      pr_debug("%s: done\n", __func__);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_start_xmit
++ */
++static int ps3_jupiter_sta_start_xmit(struct sk_buff *skb, struct net_device *netdev)
++{
++      struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev);
++
++      return ps3_jupiter_sta_tx_skb(jstad, skb);
++}
++
++/*
++ * ps3_jupiter_sta_set_rx_mode
++ */
++static void ps3_jupiter_sta_set_rx_mode(struct net_device *netdev)
++{
++      /* XXX: implement */
++}
++
++/*
++ * ps3_jupiter_sta_change_mtu
++ */
++static int ps3_jupiter_sta_change_mtu(struct net_device *netdev, int new_mtu)
++{
++      /* XXX: implement */
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_tx_timeout
++ */
++static void ps3_jupiter_sta_tx_timeout(struct net_device *netdev)
++{
++      /* XXX: implement */
++}
++
++/*
++ * ps3_jupiter_sta_get_drvinfo
++ */
++static void ps3_jupiter_sta_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
++{
++      /* XXX: implement */
++}
++
++/*
++ * ps3_jupiter_sta_get_link
++ */
++static u32 ps3_jupiter_sta_get_link(struct net_device *netdev)
++{
++      /* XXX: implement */
++
++      return 0;
++}
++
++static const iw_handler ps3_jupiter_sta_iw_handler[] =
++{
++      IW_HANDLER(SIOCGIWNAME,         ps3_jupiter_sta_get_name),
++      IW_HANDLER(SIOCGIWNICKN,        ps3_jupiter_sta_get_nick),
++      IW_HANDLER(SIOCGIWRANGE,        ps3_jupiter_sta_get_range),
++      IW_HANDLER(SIOCSIWMODE,         ps3_jupiter_sta_set_mode),
++      IW_HANDLER(SIOCGIWMODE,         ps3_jupiter_sta_get_mode),
++      IW_HANDLER(SIOCSIWFREQ,         ps3_jupiter_sta_set_freq),
++      IW_HANDLER(SIOCGIWFREQ,         ps3_jupiter_sta_get_freq),
++      IW_HANDLER(SIOCSIWSCAN,         ps3_jupiter_sta_set_scan),
++      IW_HANDLER(SIOCGIWSCAN,         ps3_jupiter_sta_get_scan),
++      IW_HANDLER(SIOCSIWAUTH,         ps3_jupiter_sta_set_auth),
++      IW_HANDLER(SIOCGIWAUTH,         ps3_jupiter_sta_get_auth),
++      IW_HANDLER(SIOCSIWESSID,        ps3_jupiter_sta_set_essid),
++      IW_HANDLER(SIOCGIWESSID,        ps3_jupiter_sta_get_essid),
++      IW_HANDLER(SIOCSIWAP,           ps3_jupiter_sta_set_ap),
++      IW_HANDLER(SIOCGIWAP,           ps3_jupiter_sta_get_ap),
++      IW_HANDLER(SIOCSIWENCODE,       ps3_jupiter_sta_set_encode),
++      IW_HANDLER(SIOCGIWENCODE,       ps3_jupiter_sta_get_encode),
++      IW_HANDLER(SIOCSIWENCODEEXT,    ps3_jupiter_sta_set_encodeext),
++      IW_HANDLER(SIOCGIWENCODEEXT,    ps3_jupiter_sta_get_encodeext),
++      IW_HANDLER(SIOCSIWGENIE,        ps3_jupiter_sta_set_genie),
++      IW_HANDLER(SIOCGIWGENIE,        ps3_jupiter_sta_get_genie),
++      IW_HANDLER(SIOCSIWMLME,         ps3_jupiter_sta_set_mlme),
++};
++
++#ifdef CONFIG_WEXT_PRIV
++static const iw_handler ps3_jupiter_sta_iw_priv_handler[] = {
++      ps3_jupiter_sta_set_opmode,
++      ps3_jupiter_sta_get_opmode,
++};
++
++enum {
++      PS3_JUPITER_STA_IW_PRIV_SET_OPMODE = SIOCIWFIRSTPRIV,
++      PS3_JUPITER_STA_IW_PRIV_GET_OPMODE,
++};
++
++static struct iw_priv_args ps3_jupiter_sta_iw_priv_args[] = {
++      {
++              .cmd = PS3_JUPITER_STA_IW_PRIV_SET_OPMODE,
++              .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
++              .name = "set_opmode"
++      },
++      {
++              .cmd = PS3_JUPITER_STA_IW_PRIV_GET_OPMODE,
++              .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
++              .name = "get_opmode"
++      },
++};
++#endif
++
++static const struct iw_handler_def ps3_jupiter_sta_iw_handler_def = {
++      .standard               = ps3_jupiter_sta_iw_handler,
++      .num_standard           = ARRAY_SIZE(ps3_jupiter_sta_iw_handler),
++#ifdef CONFIG_WEXT_PRIV
++      .private                = ps3_jupiter_sta_iw_priv_handler,
++      .num_private            = ARRAY_SIZE(ps3_jupiter_sta_iw_priv_handler),
++      .private_args           = ps3_jupiter_sta_iw_priv_args,
++      .num_private_args       = ARRAY_SIZE(ps3_jupiter_sta_iw_priv_args),
++#endif
++      .get_wireless_stats     = ps3_jupiter_sta_get_wireless_stats,
++};
++
++static const struct net_device_ops ps3_jupiter_sta_net_device_ops = {
++      .ndo_open               = ps3_jupiter_sta_open,
++      .ndo_stop               = ps3_jupiter_sta_stop,
++      .ndo_start_xmit         = ps3_jupiter_sta_start_xmit,
++      .ndo_set_rx_mode        = ps3_jupiter_sta_set_rx_mode,
++      .ndo_change_mtu         = ps3_jupiter_sta_change_mtu,
++      .ndo_tx_timeout         = ps3_jupiter_sta_tx_timeout,
++      .ndo_set_mac_address    = eth_mac_addr,
++      .ndo_validate_addr      = eth_validate_addr,
++};
++
++static const struct ethtool_ops ps3_jupiter_sta_ethtool_ops = {
++      .get_drvinfo    = ps3_jupiter_sta_get_drvinfo,
++      .get_link       = ps3_jupiter_sta_get_link,
++};
++
++/*
++ * ps3_jupiter_sta_rx_urb_complete
++ */
++static void ps3_jupiter_sta_rx_urb_complete(struct urb *urb)
++{
++      struct ps3_jupiter_sta_dev *jstad = usb_get_intfdata(usb_ifnum_to_if(urb->dev, PS3_JUPITER_STA_IFACE));
++      struct usb_device *udev = jstad->udev;
++      struct net_device *netdev = jstad->netdev;
++      struct sk_buff *skb = urb->context;
++      int err;
++
++      dev_dbg(&udev->dev, "Rx URB completed (%d)\n", urb->status);
++
++      switch (urb->status) {
++      case 0:
++              /* success */
++
++              if (urb->actual_length == 0x10) {
++                      dev_info(&udev->dev, "got empty Rx URB\n");
++                      break;
++              }
++
++              skb_put(skb, urb->actual_length);
++
++              err = ps3_jupiter_sta_prepare_rx_urb(jstad, urb);
++              if (err) {
++                      dev_err(&udev->dev, "could not prepare Rx URB (%d)\n", err);
++                      break;
++              }
++
++              skb_queue_tail(&jstad->rx_skb_queue, skb);
++
++              tasklet_schedule(&jstad->rx_tasklet);
++      break;
++      case -ECONNRESET:
++      case -ENOENT:
++      case -ESHUTDOWN:
++      case -ENODEV:
++              goto free_skb;
++      break;
++      default:
++              netdev->stats.rx_errors++;
++              dev_err(&udev->dev, "Rx URB failed (%d)\n", urb->status);
++      }
++
++      /* resubmit */
++
++      skb = urb->context;
++      skb_reset_tail_pointer(skb);
++      skb_trim(skb, 0);
++
++      usb_anchor_urb(urb, &jstad->rx_urb_anchor);
++
++      err = usb_submit_urb(urb, GFP_ATOMIC);
++      if (err) {
++              netdev->stats.rx_errors++;
++              usb_unanchor_urb(urb);
++              dev_err(&udev->dev, "could not submit Rx URB (%d)\n", err);
++              goto free_skb;
++      }
++
++      return;
++
++free_skb:
++
++      dev_kfree_skb_irq(skb);
++}
++
++/*
++ * ps3_jupiter_sta_tx_urb_complete
++ */
++static void ps3_jupiter_sta_tx_urb_complete(struct urb *urb)
++{
++      struct ps3_jupiter_sta_dev *jstad = usb_get_intfdata(usb_ifnum_to_if(urb->dev, PS3_JUPITER_STA_IFACE));
++      struct usb_device *udev = jstad->udev;
++      struct net_device *netdev = jstad->netdev;
++      struct sk_buff *skb = urb->context;
++      unsigned long irq_flags;
++
++      dev_dbg(&udev->dev, "Tx URB completed (%d)\n", urb->status);
++
++      switch (urb->status) {
++      case 0:
++              /* success */
++      
++              spin_lock_irqsave(&jstad->lock, irq_flags);
++
++              netdev->stats.tx_packets++;
++              netdev->stats.tx_bytes += skb->len;
++
++              spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++              atomic_dec(&jstad->tx_submitted_urbs);
++      break;
++      case -ECONNRESET:
++      case -ENOENT:
++      case -ESHUTDOWN:
++      case -ENODEV:
++      break;
++      default:
++              netdev->stats.tx_errors++;
++              dev_err(&udev->dev, "Tx URB failed (%d)\n", urb->status);
++      }
++
++      dev_kfree_skb_irq(skb);
++}
++
++/*
++ * ps3_jupiter_sta_tx_skb
++ */
++static int ps3_jupiter_sta_tx_skb(struct ps3_jupiter_sta_dev *jstad, struct sk_buff *skb)
++{
++      struct usb_device *udev = jstad->udev;
++      struct net_device *netdev = jstad->netdev;
++      struct urb *urb;
++      unsigned long irq_flags;
++      int err;
++
++      pr_debug("%s: called\n", __func__);
++
++      if (jstad->assoc_status != PS3_JUPITER_STA_ASSOC_OK) {
++              err = 0;
++              goto drop;
++      }
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++
++      if (atomic_read(&jstad->tx_submitted_urbs) >= max_txurbs) {
++              spin_unlock_irqrestore(&jstad->lock, irq_flags);
++              dev_err(&udev->dev, "no free Tx URBs\n");
++              err = -EBUSY;
++              goto drop;
++      }
++
++      atomic_inc(&jstad->tx_submitted_urbs);
++
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++
++      urb = usb_alloc_urb(0, GFP_ATOMIC);
++      if (!urb) {
++              atomic_dec(&jstad->tx_submitted_urbs);
++              dev_err(&udev->dev, "could not allocate Tx URB\n");
++              err = -ENOMEM;
++              goto drop;
++      }
++
++      usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, PS3_JUPITER_STA_EP),
++          skb->data, skb->len, ps3_jupiter_sta_tx_urb_complete, skb);
++      urb->transfer_flags |= URB_ZERO_PACKET;
++
++      usb_anchor_urb(urb, &jstad->tx_urb_anchor);
++      usb_free_urb(urb);
++
++      err = usb_submit_urb(urb, GFP_ATOMIC);
++      if (err) {
++              atomic_dec(&jstad->tx_submitted_urbs);
++              usb_unanchor_urb(urb);
++              dev_err(&udev->dev, "could not submit Tx URB (%d)\n", err);
++              goto drop;
++      }
++
++      pr_debug("%s: done\n", __func__);
++
++      return 0;
++
++drop:
++
++      spin_lock_irqsave(&jstad->lock, irq_flags);
++      netdev->stats.tx_dropped++;
++      spin_unlock_irqrestore(&jstad->lock, irq_flags);
++      dev_kfree_skb_any(skb);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_free_scan_results
++ */
++static void ps3_jupiter_sta_free_scan_results(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct ps3_jupiter_sta_scan_result *scan_result, *tmp;
++
++      list_for_each_entry_safe(scan_result, tmp, &jstad->scan_result_list, list) {
++              list_del(&scan_result->list);
++              kfree(scan_result);
++      }
++}
++
++/*
++ * ps3_jupiter_sta_start_scan
++ */
++static int ps3_jupiter_sta_start_scan(struct ps3_jupiter_sta_dev *jstad,
++      u8 *essid, size_t essid_length, u16 channels, u8 active, u16 channel_dwell)
++{
++      struct ps3_eurus_cmd_start_scan *eurus_cmd_start_scan;
++      struct usb_device *udev = jstad->udev;
++      unsigned char *buf = NULL;
++      unsigned int payload_length, status;
++      unsigned int i, chan;
++      u8 *chan_ie, *essid_ie;
++      int err;
++
++      pr_debug("%s: called\n", __func__);
++
++#ifdef DEBUG
++      if (essid && essid_length)
++              pr_debug("%s: essid %s\n", __func__, essid);
++#endif
++
++      if (mutex_lock_interruptible(&jstad->scan_lock))
++              return -ERESTARTSYS;
++
++      if (jstad->scan_status == PS3_JUPITER_STA_SCAN_IN_PROGRESS) {
++              err = 0;
++              goto done;
++      }
++
++      dev_dbg(&udev->dev, "starting new scan\n");
++
++      buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL);
++      if (!buf) {
++              err = -ENOMEM;
++              goto done;
++      }
++
++      eurus_cmd_start_scan = (struct ps3_eurus_cmd_start_scan *) buf;
++      memset(eurus_cmd_start_scan, 0, 0x100);
++      eurus_cmd_start_scan->unknown2 = active;
++      eurus_cmd_start_scan->channel_dwell = cpu_to_le16(channel_dwell);
++
++      chan_ie = eurus_cmd_start_scan->ie;
++      chan_ie[0] = WLAN_EID_DS_PARAMS;        /* ie id */
++      chan_ie[1] = 0x0;                       /* ie length */
++
++      for (i = 0, chan = 0; i < ARRAY_SIZE(ps3_jupiter_sta_channel_freq); i++) {
++              if (channels & (1 << i)) {
++                      chan_ie[2 + chan] = i + 1;
++                      chan++;
++              }
++      }
++
++      chan_ie[1] = chan; /* ie length */
++      payload_length = chan_ie + 2 + chan_ie[1] - (u8 *) eurus_cmd_start_scan;
++
++      if (essid && essid_length) {
++              essid_ie = chan_ie + 2 + chan_ie[1];
++              essid_ie[0] = WLAN_EID_SSID;    /* ie id */
++              essid_ie[1] = essid_length;     /* ie length */
++              memcpy(essid_ie + 2, essid, essid_length);
++      
++              payload_length += 2 + essid_ie[1];
++      }
++
++      init_completion(&jstad->scan_done_comp);
++
++      jstad->scan_status = PS3_JUPITER_STA_SCAN_IN_PROGRESS;
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_START_SCAN,
++          eurus_cmd_start_scan, payload_length, &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      err = 0;
++
++      pr_debug("%s: done\n", __func__);
++
++done:
++
++      if (err)
++              jstad->scan_status = PS3_JUPITER_STA_SCAN_INVALID;
++
++      if (buf)
++              kfree(buf);
++
++      mutex_unlock(&jstad->scan_lock);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_scan_results
++ */
++static int ps3_jupiter_sta_get_scan_results(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct ps3_eurus_cmd_get_scan_results *eurus_cmd_get_scan_results;
++      struct ps3_eurus_scan_result *eurus_scan_result;
++      struct ps3_jupiter_sta_scan_result *scan_result;
++      unsigned char *buf;
++      unsigned int status, response_length;
++      size_t eurus_scan_result_length, ie_length;
++      unsigned int i;
++      u8 *ie;
++      int err;
++
++      pr_debug("%s: called\n", __func__);
++
++      buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      eurus_cmd_get_scan_results = (struct ps3_eurus_cmd_get_scan_results *) buf;
++      memset(eurus_cmd_get_scan_results, 0, PS3_EURUS_SCAN_RESULTS_MAXSIZE);
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_GET_SCAN_RESULTS,
++          eurus_cmd_get_scan_results, PS3_EURUS_SCAN_RESULTS_MAXSIZE, &status,
++          &response_length, eurus_cmd_get_scan_results);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* free old scan results */
++
++      ps3_jupiter_sta_free_scan_results(jstad);
++
++      pr_debug("%s: number of scan results %d\n", __func__, eurus_cmd_get_scan_results->count);
++
++      /* add each scan result to list */
++
++      for (i = 0, eurus_scan_result = eurus_cmd_get_scan_results->result;
++           i < eurus_cmd_get_scan_results->count; i++) {
++              eurus_scan_result_length = le16_to_cpu(eurus_scan_result->length) +
++                  sizeof(eurus_scan_result->length);
++              ie_length = (u8 *) eurus_scan_result + eurus_scan_result_length - eurus_scan_result->ie;
++
++              scan_result = kzalloc(sizeof(*scan_result) + ie_length, GFP_KERNEL);
++              if (!scan_result)
++                      goto next;
++
++              memcpy(scan_result->bssid, eurus_scan_result->bssid, sizeof(eurus_scan_result->bssid));
++              scan_result->capability = le16_to_cpu(eurus_scan_result->capability);
++              scan_result->rssi = eurus_scan_result->rssi;
++
++              memcpy(scan_result->ie, eurus_scan_result->ie, ie_length);
++              scan_result->ie_length = ie_length;
++
++              for (ie = scan_result->ie;
++                   ie < (scan_result->ie + scan_result->ie_length);
++                   ie += (2 + ie[1])) {
++                      switch (ie[0]) {
++                      case WLAN_EID_SSID:
++                              scan_result->essid_ie = ie;
++                      break;
++                      case WLAN_EID_SUPP_RATES:
++                              scan_result->supp_rates_ie = ie;
++                      break;
++                      case WLAN_EID_DS_PARAMS:
++                              scan_result->ds_param_set_ie = ie;
++                      break;
++                      case WLAN_EID_RSN:
++                              scan_result->rsn_ie = ie;
++                      break;
++                      case WLAN_EID_EXT_SUPP_RATES:
++                              scan_result->ext_supp_rates_ie = ie;
++                      break;
++                      case WLAN_EID_VENDOR_SPECIFIC:
++                      {
++                              /* WPA */
++
++                              static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2 };
++
++                              if (((sizeof(wpa_oui) + 1) <= ie[1]) &&
++                                  !memcmp(&ie[2], wpa_oui, sizeof(wpa_oui)) &&
++                                  (ie[2 + sizeof(wpa_oui)] == 0x1))
++                                      scan_result->wpa_ie = ie;
++                      }
++                      break;
++                      }
++              }
++
++              list_add_tail(&scan_result->list, &jstad->scan_result_list);
++
++      next:
++
++              /* move to next scan result */
++
++              eurus_scan_result = (struct ps3_eurus_scan_result *) ((u8 *) eurus_scan_result +
++                  eurus_scan_result_length);
++      }
++
++      err = 0;
++
++      pr_debug("%s: done\n", __func__);
++
++done:
++
++      kfree(buf);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_translate_scan_result
++ */
++static char *ps3_jupiter_sta_translate_scan_result(struct ps3_jupiter_sta_dev *jstad,
++      struct ps3_jupiter_sta_scan_result *scan_result,
++      struct iw_request_info *info, char *stream, char *ends)
++{
++      struct iw_event iwe;
++      char *tmp;
++      unsigned int i;
++
++      memset(&iwe, 0, sizeof(iwe));
++      iwe.cmd = SIOCGIWAP;
++      iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
++      memcpy(iwe.u.ap_addr.sa_data, scan_result->bssid, ETH_ALEN);
++      stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_ADDR_LEN);
++
++      if (scan_result->essid_ie) {
++              memset(&iwe, 0, sizeof(iwe));
++              iwe.cmd = SIOCGIWESSID;
++              iwe.u.data.flags = 1;
++              iwe.u.data.length = scan_result->essid_ie[1];
++              stream = iwe_stream_add_point(info, stream, ends, &iwe, &scan_result->essid_ie[2]);
++      }
++
++      if (scan_result->ds_param_set_ie) {
++              memset(&iwe, 0, sizeof(iwe));
++              iwe.cmd = SIOCGIWFREQ;
++              iwe.u.freq.m = scan_result->ds_param_set_ie[2];
++              iwe.u.freq.e = 0;
++              iwe.u.freq.i = 0;
++              stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_FREQ_LEN);
++      }
++
++      tmp = stream + iwe_stream_lcp_len(info);
++
++      if (scan_result->supp_rates_ie) {
++              for (i = 0; i < scan_result->supp_rates_ie[1]; i++) {
++                      memset(&iwe, 0, sizeof(iwe));
++                      iwe.cmd = SIOCGIWRATE;
++                      iwe.u.bitrate.fixed = 0;
++                      iwe.u.bitrate.disabled = 0;
++                      iwe.u.bitrate.value = (scan_result->supp_rates_ie[2 + i] & 0x7f) * 500000;
++                      tmp = iwe_stream_add_value(info, stream, tmp, ends, &iwe, IW_EV_PARAM_LEN);
++              }
++      }
++
++      if (scan_result->ext_supp_rates_ie) {
++              for (i = 0; i < scan_result->ext_supp_rates_ie[1]; i++) {
++                      memset(&iwe, 0, sizeof(iwe));
++                      iwe.cmd = SIOCGIWRATE;
++                      iwe.u.bitrate.fixed = 0;
++                      iwe.u.bitrate.disabled = 0;
++                      iwe.u.bitrate.value = (scan_result->ext_supp_rates_ie[2 + i] & 0x7f) * 500000;
++                      tmp = iwe_stream_add_value(info, stream, tmp, ends, &iwe, IW_EV_PARAM_LEN);
++              }
++      }
++
++      stream = tmp;
++
++      iwe.cmd = SIOCGIWMODE;
++      if (scan_result->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
++              if (scan_result->capability & WLAN_CAPABILITY_ESS)
++                      iwe.u.mode = IW_MODE_MASTER;
++              else
++                      iwe.u.mode = IW_MODE_ADHOC;
++              stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_UINT_LEN);
++      }
++
++      memset(&iwe, 0, sizeof(iwe));
++      iwe.cmd = SIOCGIWENCODE;
++      if (scan_result->capability & WLAN_CAPABILITY_PRIVACY)
++              iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
++      else
++              iwe.u.data.flags = IW_ENCODE_DISABLED;
++      iwe.u.data.length = 0;
++      stream = iwe_stream_add_point(info, stream, ends, &iwe, scan_result->bssid);
++
++      if (scan_result->rsn_ie) {
++              memset(&iwe, 0, sizeof(iwe));
++              iwe.cmd = IWEVGENIE;
++              iwe.u.data.length = 2 + scan_result->rsn_ie[1];
++              stream = iwe_stream_add_point(info, stream, ends, &iwe, scan_result->rsn_ie);
++      }
++
++      if (scan_result->wpa_ie) {
++              memset(&iwe, 0, sizeof(iwe));
++              iwe.cmd = IWEVGENIE;
++              iwe.u.data.length = 2 + scan_result->wpa_ie[1];
++              stream = iwe_stream_add_point(info, stream, ends, &iwe, scan_result->wpa_ie);
++      }
++
++      memset(&iwe, 0, sizeof(iwe));
++      iwe.cmd = IWEVQUAL;
++      iwe.u.qual.updated  = IW_QUAL_ALL_UPDATED | IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
++      iwe.u.qual.level = ps3_eurus_rssi2percentage(scan_result->rssi);
++      iwe.u.qual.qual = ps3_eurus_rssi2percentage(scan_result->rssi);
++      iwe.u.qual.noise = 0;
++      stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_QUAL_LEN);
++
++      return stream;
++}
++
++/*
++ * ps3_jupiter_sta_find_best_scan_result
++ */
++static struct ps3_jupiter_sta_scan_result *ps3_jupiter_sta_find_best_scan_result(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct ps3_jupiter_sta_scan_result *scan_result, *best_scan_result;
++      u8 *essid;
++      unsigned int essid_length;
++
++      best_scan_result = NULL;
++
++      /* traverse scan results */
++
++      list_for_each_entry(scan_result, &jstad->scan_result_list, list) {
++              if (scan_result->essid_ie) {
++                      essid = &scan_result->essid_ie[2];
++                      essid_length = scan_result->essid_ie[1];
++              } else {
++                      essid = NULL;
++                      essid_length = 0;
++              }
++
++              if ((essid_length != jstad->essid_length) ||
++                  strncmp(essid, jstad->essid, essid_length))
++                      continue;
++
++              if (test_bit(PS3_JUPITER_STA_CONFIG_BSSID_SET, &jstad->config_status)) {
++                      if (!compare_ether_addr(jstad->desired_bssid, scan_result->bssid)) {
++                              best_scan_result = scan_result;
++                              break;
++                      } else {
++                              continue;
++                      }
++              }
++
++              switch (jstad->wpa_mode) {
++              case PS3_JUPITER_STA_WPA_MODE_NONE:
++                      if ((jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_WEP) &&
++                          !(scan_result->capability & WLAN_CAPABILITY_PRIVACY))
++                              continue;
++              break;
++              case PS3_JUPITER_STA_WPA_MODE_WPA:
++                      if (!scan_result->wpa_ie)
++                              continue;
++              break;
++              case PS3_JUPITER_STA_WPA_MODE_WPA2:
++                      if (!scan_result->rsn_ie)
++                              continue;
++              break;
++              }
++
++              if (!best_scan_result || (best_scan_result->rssi > scan_result->rssi))
++                      best_scan_result = scan_result;
++      }
++
++      return best_scan_result;
++}
++
++/*
++ * ps3_jupiter_sta_assoc
++ */
++static int ps3_jupiter_sta_assoc(struct ps3_jupiter_sta_dev *jstad,
++      struct ps3_jupiter_sta_scan_result *scan_result)
++{
++      struct ps3_eurus_cmd_0x1ed *eurus_cmd_0x1ed;
++      struct ps3_eurus_cmd_0x1025 *eurus_cmd_0x1025;
++      struct ps3_eurus_cmd_common_config *eurus_cmd_common_config;
++      struct ps3_eurus_cmd_wep_config *eurus_cmd_wep_config;
++      struct ps3_eurus_cmd_wpa_config *eurus_cmd_wpa_config;
++      struct ps3_eurus_cmd_associate *eurus_cmd_associate;
++      unsigned char *buf = NULL;
++      unsigned int payload_length, status;
++      u8 *ie;
++      int err;
++
++      buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      eurus_cmd_0x1ed = (struct ps3_eurus_cmd_0x1ed *) buf;
++      memset(eurus_cmd_0x1ed, 0, sizeof(*eurus_cmd_0x1ed));
++      eurus_cmd_0x1ed->unknown2 = 0x1;
++      eurus_cmd_0x1ed->unknown3 = 0x2;
++      eurus_cmd_0x1ed->unknown4 = 0xff;
++      eurus_cmd_0x1ed->unknown5 = 0x16;       /*XXX: 0x4 if AP doesn't support rate 54Mbps */
++      eurus_cmd_0x1ed->unknown6 = 0x4;
++      eurus_cmd_0x1ed->unknown7 = 0xa;
++      eurus_cmd_0x1ed->unknown8 = 0x16;       /*XXX: 0x4 if AP doesn't support rate 54Mbps */
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_0x1ed,
++          eurus_cmd_0x1ed, sizeof(*eurus_cmd_0x1ed), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* set preamble mode */
++
++      eurus_cmd_0x1025 = (struct ps3_eurus_cmd_0x1025 *) buf;
++      memset(eurus_cmd_0x1025, 0, sizeof(*eurus_cmd_0x1025));
++
++      if (scan_result->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
++              eurus_cmd_0x1025->preamble_mode = PS3_EURUS_PREAMBLE_SHORT;
++      else
++              eurus_cmd_0x1025->preamble_mode = PS3_EURUS_PREAMBLE_LONG;
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_0x1025,
++          eurus_cmd_0x1025, sizeof(*eurus_cmd_0x1025), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* set common configuration */
++
++      eurus_cmd_common_config = (struct ps3_eurus_cmd_common_config *) buf;
++      memset(eurus_cmd_common_config, 0, sizeof(*eurus_cmd_common_config));
++
++      switch (jstad->bss_type) {
++      case PS3_JUPITER_STA_BSS_TYPE_INFRA:
++              eurus_cmd_common_config->bss_type = PS3_EURUS_BSS_INFRA;
++      break;
++      case PS3_JUPITER_STA_BSS_TYPE_ADHOC:
++              eurus_cmd_common_config->bss_type = PS3_EURUS_BSS_ADHOC;
++      break;
++      }
++
++      switch (jstad->auth_mode) {
++      case PS3_JUPITER_STA_AUTH_OPEN:
++              eurus_cmd_common_config->auth_mode = PS3_EURUS_AUTH_OPEN;
++      break;
++      case PS3_JUPITER_STA_AUTH_SHARED_KEY:
++              eurus_cmd_common_config->auth_mode = PS3_EURUS_AUTH_SHARED_KEY;
++      break;
++      }
++
++      switch (jstad->opmode) {
++      case PS3_JUPITER_STA_OPMODE_11B:
++              eurus_cmd_common_config->opmode = PS3_EURUS_OPMODE_11B;
++      break;
++      case PS3_JUPITER_STA_OPMODE_11G:
++              eurus_cmd_common_config->opmode = PS3_EURUS_OPMODE_11G;
++      break;
++      case PS3_JUPITER_STA_OPMODE_11BG:
++              eurus_cmd_common_config->opmode = PS3_EURUS_OPMODE_11BG;
++      break;
++      }
++
++      memcpy(eurus_cmd_common_config->bssid, scan_result->bssid, sizeof(scan_result->bssid));
++
++      eurus_cmd_common_config->capability = cpu_to_le16(scan_result->capability & ~WLAN_CAPABILITY_QOS);
++
++      payload_length = sizeof(*eurus_cmd_common_config);
++
++      ie = eurus_cmd_common_config->ie;
++
++      ie[0] = WLAN_EID_SSID;
++      ie[1] = jstad->essid_length;
++      memcpy(&ie[2], jstad->essid, jstad->essid_length);
++
++      payload_length += (2 + ie[1]);
++      ie += (2 + ie[1]);
++
++      if (scan_result->ds_param_set_ie) {
++              memcpy(ie, scan_result->ds_param_set_ie, 2 + scan_result->ds_param_set_ie[1]);
++
++              payload_length += (2 + ie[1]);
++              ie += (2 + ie[1]);
++      }
++
++      if (scan_result->supp_rates_ie) {
++              memcpy(ie, scan_result->supp_rates_ie, 2 + scan_result->supp_rates_ie[1]);
++
++              payload_length += (2 + scan_result->supp_rates_ie[1]);
++              ie += (2 + scan_result->supp_rates_ie[1]);
++      }
++
++      if (scan_result->ext_supp_rates_ie) {
++              memcpy(ie, scan_result->ext_supp_rates_ie, 2 + scan_result->ext_supp_rates_ie[1]);
++
++              payload_length += (2 + scan_result->ext_supp_rates_ie[1]);
++              ie += (2 + scan_result->ext_supp_rates_ie[1]);
++      }
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_COMMON_CONFIG,
++          eurus_cmd_common_config, payload_length, &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      if (jstad->wpa_mode == PS3_JUPITER_STA_WPA_MODE_NONE) {
++              /* set WEP configuration */
++
++              /* XXX: implement */
++
++              eurus_cmd_wep_config = (struct ps3_eurus_cmd_wep_config *) buf;
++              memset(eurus_cmd_wep_config, 0, sizeof(*eurus_cmd_wep_config));
++
++              err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_WEP_CONFIG,
++                  eurus_cmd_wep_config, sizeof(*eurus_cmd_wep_config), &status, NULL, NULL);
++              if (err)
++                      goto done;
++
++              if (status != PS3_EURUS_CMD_OK) {
++                      err = -EIO;
++                      goto done;
++              }
++      } else {
++              /* set WPA configuration */
++
++              eurus_cmd_wpa_config = (struct ps3_eurus_cmd_wpa_config *) buf;
++              memset(eurus_cmd_wpa_config, 0, sizeof(*eurus_cmd_wpa_config));
++
++              eurus_cmd_wpa_config->unknown = 0x1;
++
++              switch (jstad->wpa_mode) {
++              case PS3_JUPITER_STA_WPA_MODE_WPA:
++                      eurus_cmd_wpa_config->security_mode = PS3_EURUS_WPA_SECURITY_WPA;
++                      if (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP)
++                              eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_TKIP;
++                      else
++                              eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_AES;
++                      if (jstad->pairwise_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP)
++                              eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_TKIP;
++                      else
++                              eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_AES;
++                      eurus_cmd_wpa_config->akm_suite = PS3_EURUS_WPA_AKM_SUITE_WPA_PSK;
++              break;
++              case PS3_JUPITER_STA_WPA_MODE_WPA2:
++                      eurus_cmd_wpa_config->security_mode = PS3_EURUS_WPA_SECURITY_WPA2;
++                      if (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP)
++                              eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_TKIP;
++                      else
++                              eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_AES;
++                      if (jstad->pairwise_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP)
++                              eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_TKIP;
++                      else
++                              eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_AES;
++                      eurus_cmd_wpa_config->akm_suite = PS3_EURUS_WPA_AKM_SUITE_WPA2_PSK;
++              break;
++              default:
++                      /* should never happen */
++                      BUG();
++              }
++
++              eurus_cmd_wpa_config->psk_type = PS3_EURUS_WPA_PSK_BIN;
++              memcpy(eurus_cmd_wpa_config->psk, jstad->psk, sizeof(jstad->psk));
++
++              err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_WPA_CONFIG,
++                  eurus_cmd_wpa_config, sizeof(*eurus_cmd_wpa_config), &status, NULL, NULL);
++              if (err)
++                      goto done;
++
++              if (status != PS3_EURUS_CMD_OK) {
++                      err = -EIO;
++                      goto done;
++              }
++      }
++
++      init_completion(&jstad->assoc_done_comp);
++
++      jstad->assoc_status = PS3_JUPITER_STA_ASSOC_IN_PROGRESS;
++
++      eurus_cmd_associate = (struct ps3_eurus_cmd_associate *) buf;
++      memset(eurus_cmd_associate, 0, sizeof(*eurus_cmd_associate));
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_ASSOCIATE,
++          eurus_cmd_associate, sizeof(*eurus_cmd_associate), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      err = wait_for_completion_timeout(&jstad->assoc_done_comp, 5 * HZ);
++      if (!err) {
++              /* timeout */
++              ps3_jupiter_sta_disassoc(jstad);
++              err = -EIO;
++              goto done;
++      }
++
++      jstad->assoc_status = PS3_JUPITER_STA_ASSOC_OK;
++
++      memcpy(jstad->bssid, scan_result->bssid, sizeof(scan_result->bssid));
++
++      err = 0;
++
++done:
++
++      if (err)
++              jstad->assoc_status = PS3_JUPITER_STA_ASSOC_INVALID;
++
++      kfree(buf);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_assoc_worker
++ */
++static void ps3_jupiter_sta_assoc_worker(struct work_struct *work)
++{
++      struct ps3_jupiter_sta_dev *jstad = container_of(work, struct ps3_jupiter_sta_dev, assoc_work.work);
++      struct usb_device *udev = jstad->udev;
++      u8 *essid;
++      unsigned int essid_length;
++      int scan_lock = 0;
++      struct ps3_jupiter_sta_scan_result *best_scan_result;
++      int err;
++
++      mutex_lock(&jstad->assoc_lock);
++
++      if (jstad->assoc_status != PS3_JUPITER_STA_ASSOC_INVALID) {
++              mutex_unlock(&jstad->assoc_lock);
++              return;
++      }
++
++      dev_dbg(&udev->dev, "starting new association\n");
++
++      if ((jstad->scan_status != PS3_JUPITER_STA_SCAN_OK) ||
++          time_after_eq(jiffies, jstad->scan_expires)) {
++              /* start scan and wait for scan results */
++
++              if (test_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status)) {
++                      essid = jstad->essid;
++                      essid_length = jstad->essid_length;
++              } else {
++                      essid = NULL;
++                      essid_length = 0;
++              }
++
++              err = ps3_jupiter_sta_start_scan(jstad, essid, essid_length, jstad->channel_info,
++                  1, PS3_JUPITER_STA_CHANNEL_DWELL);
++              if (err)
++                      goto done;
++
++              wait_for_completion(&jstad->scan_done_comp);
++      }
++
++      mutex_lock(&jstad->scan_lock);
++      scan_lock = 1;
++
++      if (jstad->scan_status != PS3_JUPITER_STA_SCAN_OK)
++              goto done;
++
++      best_scan_result = ps3_jupiter_sta_find_best_scan_result(jstad);
++      if (!best_scan_result) {
++              dev_dbg(&udev->dev, "no suitable scan result was found\n");
++              goto done;
++      }
++
++      err = ps3_jupiter_sta_assoc(jstad, best_scan_result);
++      if (err) {
++              dev_dbg(&udev->dev, "association failed (%d)\n", err);
++              goto done;
++      }
++
++done:
++
++      if (scan_lock)
++              mutex_unlock(&jstad->scan_lock);
++
++      if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK)
++              ps3_jupiter_sta_send_iw_ap_event(jstad, jstad->bssid);
++      else
++              ps3_jupiter_sta_send_iw_ap_event(jstad, NULL);
++
++      mutex_unlock(&jstad->assoc_lock);
++}
++
++/*
++ * ps3_jupiter_sta_start_assoc
++ */
++static void ps3_jupiter_sta_start_assoc(struct ps3_jupiter_sta_dev *jstad)
++{
++      pr_debug("%s: called\n", __func__);
++
++      if (!test_bit(PS3_JUPITER_STA_READY, &jstad->status))
++              return;
++
++      if (!test_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status))
++              return;
++
++      if ((jstad->wpa_mode == PS3_JUPITER_STA_WPA_MODE_NONE) &&
++          (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_WEP) &&
++          !jstad->key_config_status)
++              return;
++
++      if ((jstad->wpa_mode != PS3_JUPITER_STA_WPA_MODE_NONE) &&
++          !test_bit(PS3_JUPITER_STA_CONFIG_WPA_PSK_SET, &jstad->config_status))
++              return;
++
++      queue_delayed_work(jstad->assoc_queue, &jstad->assoc_work, 0);
++
++      pr_debug("%s: done\n", __func__);
++}
++
++/*
++ * ps3_jupiter_sta_disassoc
++ */
++static int ps3_jupiter_sta_disassoc(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct ps3_eurus_cmd_disassociate *eurus_cmd_disassociate;
++      unsigned char *buf = NULL;
++      unsigned int status;
++      int err;
++
++      buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      eurus_cmd_disassociate = (struct ps3_eurus_cmd_disassociate *) buf;
++      memset(eurus_cmd_disassociate, 0, sizeof(*eurus_cmd_disassociate));
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_DISASSOCIATE,
++          eurus_cmd_disassociate, sizeof(*eurus_cmd_disassociate), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      err = 0;
++
++done:
++
++      kfree(buf);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_event_scan_completed
++ */
++static void ps3_jupiter_sta_event_scan_completed(struct ps3_jupiter_sta_dev *jstad)
++{
++      union iwreq_data iwrd;
++      int err;
++
++      mutex_lock(&jstad->scan_lock);
++
++      err = ps3_jupiter_sta_get_scan_results(jstad);
++      if (err)
++              goto done;
++
++      jstad->scan_expires = jiffies + PS3_JUPITER_STA_SCAN_VALID_TIME_SEC * HZ;
++      jstad->scan_status = PS3_JUPITER_STA_SCAN_OK;
++
++      complete(&jstad->scan_done_comp);
++
++      memset(&iwrd, 0, sizeof(iwrd));
++      wireless_send_event(jstad->netdev, SIOCGIWSCAN, &iwrd, NULL);
++
++done:
++
++      if (err)
++              jstad->scan_status = PS3_JUPITER_STA_SCAN_INVALID;
++
++      mutex_unlock(&jstad->scan_lock);
++}
++
++/*
++ * ps3_jupiter_sta_event_connected
++ */
++static void ps3_jupiter_sta_event_connected(struct ps3_jupiter_sta_dev *jstad, u32 event_id)
++{
++      u32 expected_event_id = 0;
++
++      switch (jstad->wpa_mode) {
++      case PS3_JUPITER_STA_WPA_MODE_NONE:
++              expected_event_id = PS3_EURUS_EVENT_CONNECTED;
++      break;
++      case PS3_JUPITER_STA_WPA_MODE_WPA:
++      case PS3_JUPITER_STA_WPA_MODE_WPA2:
++              expected_event_id = PS3_EURUS_EVENT_WPA_CONNECTED;
++      break;
++      default:
++              return;
++      }
++
++      if (expected_event_id == event_id) {
++              complete(&jstad->assoc_done_comp);
++              netif_carrier_on(jstad->netdev);
++      }
++}
++
++/*
++ * ps3_jupiter_sta_event_disconnected
++ */
++static void ps3_jupiter_sta_event_disconnected(struct ps3_jupiter_sta_dev *jstad)
++{
++      int assoc_lock = 0;
++
++      if (mutex_trylock(&jstad->assoc_lock))
++              assoc_lock = 1;
++
++      ps3_jupiter_sta_disassoc(jstad);
++
++      if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK)
++              ps3_jupiter_sta_send_iw_ap_event(jstad, NULL);
++
++      jstad->assoc_status = PS3_JUPITER_STA_ASSOC_INVALID;
++
++      netif_carrier_off(jstad->netdev);
++
++      if (assoc_lock)
++              mutex_unlock(&jstad->assoc_lock);
++}
++
++/*
++ * ps3_jupiter_sta_event_handler
++ */
++static int ps3_jupiter_sta_event_handler(struct notifier_block *n,
++      unsigned long val, void *v)
++{
++      struct ps3_jupiter_sta_dev *jstad = container_of(n, struct ps3_jupiter_sta_dev, event_listener);
++      struct usb_device *udev = jstad->udev;
++      struct ps3_eurus_event *event = v;
++
++      dev_dbg(&udev->dev, "got event (0x%08x 0x%08x 0x%08x 0x%08x 0x%08x)\n",
++          event->hdr.type, event->hdr.id, event->hdr.timestamp,
++          event->hdr.payload_length, event->hdr.unknown);
++
++      switch (event->hdr.type) {
++      case PS3_EURUS_EVENT_TYPE_0x40:
++              switch (event->hdr.id) {
++              case PS3_EURUS_EVENT_DEAUTH:
++                      ps3_jupiter_sta_event_disconnected(jstad);
++              break;
++              }
++      break;
++      case PS3_EURUS_EVENT_TYPE_0x80:
++              switch (event->hdr.id) {
++              case PS3_EURUS_EVENT_SCAN_COMPLETED:
++                      ps3_jupiter_sta_event_scan_completed(jstad);
++              break;
++              case PS3_EURUS_EVENT_CONNECTED:
++              case PS3_EURUS_EVENT_WPA_CONNECTED:
++                      ps3_jupiter_sta_event_connected(jstad, event->hdr.id);
++              break;
++              case PS3_EURUS_EVENT_BEACON_LOST:
++                      ps3_jupiter_sta_event_disconnected(jstad);
++              break;
++              }
++      break;
++      }
++
++      return NOTIFY_OK;
++}
++
++/*
++ * ps3_jupiter_sta_set_mac_addr
++ */
++static int ps3_jupiter_sta_set_mac_addr(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct usb_device *udev = jstad->udev;
++      struct net_device *netdev = jstad->netdev;
++      struct ps3_eurus_cmd_get_mac_addr_list *eurus_cmd_get_mac_addr_list;
++      struct ps3_eurus_cmd_set_mac_addr *eurus_cmd_set_mac_addr;
++      struct ps3_eurus_cmd_0x115b *eurus_cmd_0x115b;
++      unsigned char *buf = NULL;
++      unsigned int status, response_length;
++      int err;
++
++      buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      /* get MAC address list */
++
++      eurus_cmd_get_mac_addr_list = (struct ps3_eurus_cmd_get_mac_addr_list *) buf;
++      memset(eurus_cmd_get_mac_addr_list, 0, PS3_EURUS_MAC_ADDR_LIST_MAXSIZE);
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_GET_MAC_ADDR_LIST,
++          eurus_cmd_get_mac_addr_list, PS3_EURUS_MAC_ADDR_LIST_MAXSIZE, &status,
++          &response_length, eurus_cmd_get_mac_addr_list);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      /* use first MAC address */
++
++      memcpy(netdev->dev_addr, eurus_cmd_get_mac_addr_list->mac_addr, ETH_ALEN);
++
++      dev_info(&udev->dev, "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
++          netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
++          netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
++
++      /* set MAC address */
++
++      eurus_cmd_set_mac_addr = (struct ps3_eurus_cmd_set_mac_addr *) buf;
++      memset(eurus_cmd_set_mac_addr, 0, sizeof(*eurus_cmd_set_mac_addr));
++      memcpy(eurus_cmd_set_mac_addr->mac_addr, netdev->dev_addr, ETH_ALEN);
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_MAC_ADDR,
++          eurus_cmd_set_mac_addr, sizeof(*eurus_cmd_set_mac_addr), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      eurus_cmd_0x115b = (struct ps3_eurus_cmd_0x115b *) buf;
++      memset(eurus_cmd_0x115b, 0, sizeof(*eurus_cmd_0x115b));
++      eurus_cmd_0x115b->unknown1 = cpu_to_le16(0x1);
++      eurus_cmd_0x115b->unknown2 = cpu_to_le16(0x0);
++      memcpy(eurus_cmd_0x115b->mac_addr, netdev->dev_addr, ETH_ALEN);
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_0x115b,
++          eurus_cmd_0x115b, sizeof(*eurus_cmd_0x115b), &status, NULL, NULL);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      err = 0;
++
++done:
++
++      kfree(buf);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_get_channel_info
++ */
++static int ps3_jupiter_sta_get_channel_info(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct ps3_eurus_cmd_get_channel_info *eurus_cmd_get_channel_info;
++      unsigned char *buf = NULL;
++      unsigned int status, response_length;
++      int err;
++
++      buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++
++      eurus_cmd_get_channel_info = (struct ps3_eurus_cmd_get_channel_info *) buf;
++      memset(eurus_cmd_get_channel_info, 0, sizeof(*eurus_cmd_get_channel_info));
++
++      err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_GET_CHANNEL_INFO,
++          eurus_cmd_get_channel_info, sizeof(*eurus_cmd_get_channel_info), &status,
++          &response_length, eurus_cmd_get_channel_info);
++      if (err)
++              goto done;
++
++      if (status != PS3_EURUS_CMD_OK) {
++              err = -EIO;
++              goto done;
++      }
++
++      jstad->channel_info = eurus_cmd_get_channel_info->channel_info;
++
++      err = 0;
++
++done:
++
++      kfree(buf);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_reset_state
++ */
++static void ps3_jupiter_sta_reset_state(struct ps3_jupiter_sta_dev *jstad)
++{
++      jstad->scan_status = PS3_JUPITER_STA_SCAN_INVALID;
++
++      jstad->config_status = 0;
++
++      jstad->opmode = PS3_JUPITER_STA_OPMODE_11G;
++
++      jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN;
++
++      jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE;
++      jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++      jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE;
++
++      memset(jstad->essid, 0, sizeof(jstad->essid));
++      jstad->essid_length = 0;
++
++      memset(jstad->desired_bssid, 0, sizeof(jstad->desired_bssid));
++
++      jstad->channel = 0;
++
++      jstad->key_config_status = 0;
++      jstad->curr_key_index = 0;
++
++      jstad->assoc_status = PS3_JUPITER_STA_ASSOC_INVALID;
++}
++
++/*
++ * ps3_jupiter_sta_create_assoc_worker
++ */
++static int ps3_jupiter_sta_create_assoc_worker(struct ps3_jupiter_sta_dev *jstad)
++{
++      jstad->assoc_queue = create_singlethread_workqueue("ps3_jupiter_sta_assoc");
++      if (!jstad->assoc_queue)
++              return -ENOMEM;
++
++      INIT_DELAYED_WORK(&jstad->assoc_work, ps3_jupiter_sta_assoc_worker);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_destroy_assoc_worker
++ */
++static void ps3_jupiter_sta_destroy_assoc_worker(struct ps3_jupiter_sta_dev *jstad)
++{
++      if (jstad->assoc_queue) {
++              cancel_delayed_work(&jstad->assoc_work);
++              flush_workqueue(jstad->assoc_queue);
++              destroy_workqueue(jstad->assoc_queue);
++              jstad->assoc_queue = NULL;
++      }
++}
++
++/*
++ * ps3_jupiter_sta_prepare_rx_urb
++ */
++static int ps3_jupiter_sta_prepare_rx_urb(struct ps3_jupiter_sta_dev *jstad,
++      struct urb *urb)
++{
++      struct usb_device *udev = jstad->udev;
++      struct sk_buff *skb;
++
++      skb = dev_alloc_skb(PS3_JUPITER_STA_RX_BUFSIZE);
++      if (!skb)
++              return -ENOMEM;
++
++      usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, PS3_JUPITER_STA_EP),
++          skb->data, PS3_JUPITER_STA_RX_BUFSIZE, ps3_jupiter_sta_rx_urb_complete, skb);
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_alloc_rx_urbs
++ */
++static int ps3_jupiter_sta_alloc_rx_urbs(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct usb_device *udev = jstad->udev;
++      struct urb *urb;
++      unsigned int i;
++      int err;
++
++      pr_debug("%s: called\n", __func__);
++
++      init_usb_anchor(&jstad->rx_urb_anchor);
++
++      for (i = 0; i < PS3_JUPITER_STA_RX_URBS; i++) {
++              urb = usb_alloc_urb(0, GFP_KERNEL);
++              if (!urb) {
++                      dev_err(&udev->dev, "could not allocate Rx URB\n");
++                      err = -ENOMEM;
++                      goto done;
++              }
++
++              err = ps3_jupiter_sta_prepare_rx_urb(jstad, urb);
++              if (err) {
++                      dev_err(&udev->dev, "could not prepare Rx URB (%d)\n", err);
++                      usb_free_urb(urb);
++                      goto done;
++              }
++
++              usb_anchor_urb(urb, &jstad->rx_urb_anchor);
++              usb_free_urb(urb);
++
++              err = usb_submit_urb(urb, GFP_KERNEL);
++              if (err) {
++                      dev_err(&udev->dev, "could not submit Rx URB (%d)\n", err);
++                      dev_kfree_skb_any(urb->context);
++                      usb_unanchor_urb(urb);
++                      goto done;
++              }
++      }
++
++      err = 0;
++
++      pr_debug("%s: done\n", __func__);
++
++done:
++
++      if (err)
++              usb_kill_anchored_urbs(&jstad->rx_urb_anchor);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_free_rx_urbs
++ */
++static void ps3_jupiter_sta_free_rx_urbs(struct ps3_jupiter_sta_dev *jstad)
++{
++      usb_kill_anchored_urbs(&jstad->rx_urb_anchor);
++
++      usb_poison_anchored_urbs(&jstad->rx_urb_anchor);
++}
++
++/*
++ * ps3_jupiter_sta_rx_tasklet
++ */
++static void ps3_jupiter_sta_rx_tasklet(unsigned long data)
++{
++      struct ps3_jupiter_sta_dev *jstad = (struct ps3_jupiter_sta_dev *) data;
++      struct net_device *netdev = jstad->netdev;
++      struct sk_buff *skb;
++
++      while ((skb = skb_dequeue(&jstad->rx_skb_queue))) {
++              skb->protocol = eth_type_trans(skb, netdev);
++
++              netdev->stats.rx_packets++;
++              netdev->stats.rx_bytes += skb->len;
++
++              netif_receive_skb(skb);
++      }
++}
++
++/*
++ * ps3_jupiter_sta_purge_rx_skb_queue
++ */
++static void ps3_jupiter_sta_purge_rx_skb_queue(struct ps3_jupiter_sta_dev *jstad)
++{
++      struct sk_buff *skb;
++      unsigned long irq_flags;
++
++      spin_lock_irqsave(&jstad->rx_skb_queue.lock, irq_flags);
++
++      while ((skb = __skb_dequeue(&jstad->rx_skb_queue)))
++              dev_kfree_skb_any(skb);
++
++      spin_unlock_irqrestore(&jstad->rx_skb_queue.lock, irq_flags);
++}
++
++/*
++ * ps3_jupiter_sta_free_tx_urbs
++ */
++static void ps3_jupiter_sta_free_tx_urbs(struct ps3_jupiter_sta_dev *jstad)
++{
++      usb_wait_anchor_empty_timeout(&jstad->tx_urb_anchor, msecs_to_jiffies(100));
++
++      usb_kill_anchored_urbs(&jstad->tx_urb_anchor);
++
++      usb_poison_anchored_urbs(&jstad->tx_urb_anchor);
++}
++
++/*
++ * ps3_jupiter_sta_probe
++ */
++static int ps3_jupiter_sta_probe(struct usb_interface *interface,
++      const struct usb_device_id *id)
++{
++      struct usb_device *udev = interface_to_usbdev(interface);
++      struct ps3_jupiter_sta_dev *jstad;
++      struct net_device *netdev;
++      int err;
++
++      netdev = alloc_etherdev(sizeof(struct ps3_jupiter_sta_dev));
++      if (!netdev)
++              return -ENOMEM;
++
++      SET_NETDEV_DEV(netdev, &udev->dev);
++
++      strcpy(netdev->name, "wlan%d");
++
++      netdev->ethtool_ops = &ps3_jupiter_sta_ethtool_ops;
++      netdev->netdev_ops = &ps3_jupiter_sta_net_device_ops;
++      netdev->wireless_data = &jstad->wireless_data;
++      netdev->wireless_handlers = &ps3_jupiter_sta_iw_handler_def;
++
++      jstad = netdev_priv(netdev);
++      jstad->netdev = netdev;
++
++      jstad->udev = usb_get_dev(udev);
++      usb_set_intfdata(interface, jstad);
++
++      err = ps3_jupiter_sta_set_mac_addr(jstad);
++      if (err) {
++              dev_err(&udev->dev, "could not setup network device (%d)\n", err);
++              goto fail_free_netdev;
++      }
++
++      spin_lock_init(&jstad->lock);
++
++      jstad->event_listener.notifier_call = ps3_jupiter_sta_event_handler;
++
++      err = ps3_jupiter_register_event_listener(&jstad->event_listener);
++      if (err) {
++              dev_err(&udev->dev, "could not register event listener (%d)\n", err);
++              goto fail_free_netdev;
++      }
++
++      mutex_init(&jstad->scan_lock);
++      INIT_LIST_HEAD(&jstad->scan_result_list);
++
++      err = ps3_jupiter_sta_get_channel_info(jstad);
++      if (err) {
++              dev_err(&udev->dev, "could not get channel info (%d)\n", err);
++              goto fail_unregister_event_listener;
++      }
++
++      mutex_init(&jstad->assoc_lock);
++
++      err = ps3_jupiter_sta_create_assoc_worker(jstad);
++      if (err) {
++              dev_err(&udev->dev, "could not create assoc work queue (%d)\n", err);
++              goto fail_unregister_event_listener;
++      }
++
++      skb_queue_head_init(&jstad->rx_skb_queue);
++      tasklet_init(&jstad->rx_tasklet, ps3_jupiter_sta_rx_tasklet, (unsigned long) jstad);
++
++      err = ps3_jupiter_sta_alloc_rx_urbs(jstad);
++      if (err) {
++              dev_err(&udev->dev, "could not allocate Rx URBs (%d)\n", err);
++              goto fail_destroy_assoc_worker;
++      }
++
++      init_usb_anchor(&jstad->tx_urb_anchor);
++      atomic_set(&jstad->tx_submitted_urbs, 0);
++
++      ps3_jupiter_sta_reset_state(jstad);
++
++      set_bit(PS3_JUPITER_STA_READY, &jstad->status);
++
++      err = register_netdev(netdev);
++      if (err) {
++              dev_dbg(&udev->dev, "could not register network device %s (%d)\n", netdev->name, err);
++              goto fail_free_rx_urbs;
++      }
++
++      return 0;
++
++fail_free_rx_urbs:
++
++      ps3_jupiter_sta_free_rx_urbs(jstad);
++
++fail_destroy_assoc_worker:
++
++      ps3_jupiter_sta_destroy_assoc_worker(jstad);
++
++fail_unregister_event_listener:
++
++      ps3_jupiter_unregister_event_listener(&jstad->event_listener);
++
++fail_free_netdev:
++
++      usb_set_intfdata(interface, NULL);
++      usb_put_dev(udev);
++
++      free_netdev(netdev);
++
++      return err;
++}
++
++/*
++ * ps3_jupiter_sta_disconnect
++ */
++static void ps3_jupiter_sta_disconnect(struct usb_interface *interface)
++{
++      struct ps3_jupiter_sta_dev *jstad = usb_get_intfdata(interface);
++      struct usb_device *udev = jstad->udev;
++      struct net_device *netdev = jstad->netdev;
++
++      clear_bit(PS3_JUPITER_STA_READY, &jstad->status);
++
++      unregister_netdev(netdev);
++
++      if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK)
++              ps3_jupiter_sta_disassoc(jstad);
++
++      ps3_jupiter_sta_destroy_assoc_worker(jstad);
++
++      ps3_jupiter_sta_free_rx_urbs(jstad);
++      tasklet_kill(&jstad->rx_tasklet);
++      ps3_jupiter_sta_purge_rx_skb_queue(jstad);
++
++      ps3_jupiter_sta_free_tx_urbs(jstad);
++
++      ps3_jupiter_sta_free_scan_results(jstad);
++
++      ps3_jupiter_unregister_event_listener(&jstad->event_listener);
++
++      usb_set_intfdata(interface, NULL);
++      usb_put_dev(udev);
++
++      free_netdev(netdev);
++}
++
++#ifdef CONFIG_PM
++/*
++ * ps3_jupiter_sta_suspend
++ */
++static int ps3_jupiter_sta_suspend(struct usb_interface *interface, pm_message_t state)
++{
++      /* XXX: implement */
++
++      return 0;
++}
++
++/*
++ * ps3_jupiter_sta_resume
++ */
++static int ps3_jupiter_sta_resume(struct usb_interface *interface)
++{
++      /* XXX: implement */
++
++      return 0;
++}
++#endif /* CONFIG_PM */
++
++static struct usb_device_id ps3_jupiter_sta_devtab[] = {
++      {
++              .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
++              .idVendor = 0x054c,
++              .idProduct = 0x036f,
++              .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
++              .bInterfaceSubClass = 2,
++              .bInterfaceProtocol = 2
++      },
++      { }
++};
++
++static struct usb_driver ps3_jupiter_sta_drv = {
++      .name           = KBUILD_MODNAME,
++      .id_table       = ps3_jupiter_sta_devtab,
++      .probe          = ps3_jupiter_sta_probe,
++      .disconnect     = ps3_jupiter_sta_disconnect,
++#ifdef CONFIG_PM
++      .suspend        = ps3_jupiter_sta_suspend,
++      .resume         = ps3_jupiter_sta_resume,
++#endif /* CONFIG_PM */
++};
++
++/*
++ * ps3_jupiter_sta_init
++ */
++static int __init ps3_jupiter_sta_init(void)
++{
++      return usb_register(&ps3_jupiter_sta_drv);
++}
++
++/*
++ * ps3_jupiter_sta_exit
++ */
++static void __exit ps3_jupiter_sta_exit(void)
++{
++      usb_deregister(&ps3_jupiter_sta_drv);
++}
++
++module_init(ps3_jupiter_sta_init);
++module_exit(ps3_jupiter_sta_exit);
++
++MODULE_SUPPORTED_DEVICE("PS3 Jupiter STA");
++MODULE_DEVICE_TABLE(usb, ps3_jupiter_sta_devtab);
++MODULE_DESCRIPTION("PS3 Jupiter STA");
++MODULE_AUTHOR("glevand");
++MODULE_LICENSE("GPL");
diff --git a/0160-gelic-disable-eurus-ctrl-iface.patch b/0160-gelic-disable-eurus-ctrl-iface.patch
new file mode 100644 (file)
index 0000000..13fd6bf
--- /dev/null
@@ -0,0 +1,78 @@
+--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c     2012-01-26 01:39:32.000000000 +0100
++++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c     2012-01-31 20:54:24.798382231 +0100
+@@ -1671,12 +1671,75 @@
+ {
+       struct gelic_card *card;
+       struct net_device *netdev;
++      u64 lpar_id, laid, hwconfig, eurus_lpar_access, junk;
+       int result;
+       pr_debug("%s: called\n", __func__);
+       udbg_shutdown_ps3gelic();
++      /*
++       * Check for eurus control interface and disable it when it is enabled.
++       * NB: it must be done before opening the gelic device else it will not work.
++       */
++
++      result = lv1_get_logical_partition_id(&lpar_id);
++      if (result)
++              goto open_hv_device;
++
++      result = lv1_read_repository_node(1,
++                                        0x0000000073730000ul /* ss */,
++                                        0x6c61696400000000ul /* laid */,
++                                        lpar_id,
++                                        0,
++                                        &laid, &junk);
++      if (result || (laid != 0x1070000002000001ul))
++              goto open_hv_device;
++
++      result = lv1_read_repository_node(1,
++                                        0x0000000073797300ul /* sys */,
++                                        0x6877000000000000ul /* hw */,
++                                        0x636f6e6669670000ul /* config */,
++                                        0,
++                                        &hwconfig, &junk);
++      if (result || !(hwconfig & 0x40000ul))
++              goto open_hv_device;
++
++      result = lv1_read_repository_node(1,
++                                        0x00000000696f7300ul /* ios */,
++                                        0x6e65740000000000ul /* net */,
++                                        0x6575727573000000ul /* eurus */,
++                                        0x6c70617200000000ul /* lpar */,
++                                        &eurus_lpar_access, &junk);
++      if (result)
++              goto open_hv_device;
++
++      if (!(eurus_lpar_access & (1ul << lpar_id))) {
++              dev_info(&dev->core, "%s: eurus control interface is already disabled\n",
++                       __func__);
++              goto open_hv_device;
++      } else {
++              dev_info(&dev->core, "%s: eurus control interface is enabled\n",
++                       __func__);
++      }
++
++      eurus_lpar_access &= ~(1ul << lpar_id);
++
++      result = lv1_write_repository_node(1,
++                                         0x00000000696f7300ul /* ios */,
++                                         0x6e65740000000000ul /* net */,
++                                         0x6575727573000000ul /* eurus */,
++                                         0x6c70617200000000ul /* lpar */,
++                                         eurus_lpar_access, junk);
++      if (result)
++              dev_info(&dev->core, "%s: eurus control interface could not be disabled\n",
++                       __func__);
++      else
++              dev_info(&dev->core, "%s: eurus control interface was disabled\n",
++                       __func__);
++
++open_hv_device:
++
+       result = ps3_open_hv_device(dev);
+       if (result) {
diff --git a/0170-gelic-wireless-print-cmd-status.patch b/0170-gelic-wireless-print-cmd-status.patch
new file mode 100644 (file)
index 0000000..0bddb83
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c        2012-01-26 01:39:32.000000000 +0100
++++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c        2012-02-03 15:42:14.996356826 +0100
+@@ -183,7 +183,7 @@
+                                     &cmd->tag, &cmd->size);
+       if (cmd->status) {
+               complete(&cmd->done);
+-              pr_info("%s: cmd issue failed\n", __func__);
++              pr_info("%s: cmd (%d) issue failed (%d)\n", __func__, cmd->cmd, cmd->status);
+               return;
+       }
diff --git a/0180-lv1call-add-undocumented-spe-hvcalls.patch b/0180-lv1call-add-undocumented-spe-hvcalls.patch
new file mode 100644 (file)
index 0000000..2cf35d0
--- /dev/null
@@ -0,0 +1,72 @@
+--- a/arch/powerpc/include/asm/lv1call.h       2012-07-19 22:27:03.699665219 +0200
++++ b/arch/powerpc/include/asm/lv1call.h       2012-07-19 22:27:32.146333542 +0200
+@@ -53,6 +53,7 @@
+ #define LV1_5_IN_0_OUT_ARG_DECL LV1_5_IN_ARG_DECL
+ #define LV1_6_IN_0_OUT_ARG_DECL LV1_6_IN_ARG_DECL
+ #define LV1_7_IN_0_OUT_ARG_DECL LV1_7_IN_ARG_DECL
++#define LV1_8_IN_0_OUT_ARG_DECL LV1_8_IN_ARG_DECL
+ #define LV1_0_IN_1_OUT_ARG_DECL                    LV1_1_OUT_ARG_DECL
+ #define LV1_1_IN_1_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+@@ -143,6 +144,7 @@
+ #define LV1_5_IN_0_OUT_ARGS LV1_5_IN_ARGS
+ #define LV1_6_IN_0_OUT_ARGS LV1_6_IN_ARGS
+ #define LV1_7_IN_0_OUT_ARGS LV1_7_IN_ARGS
++#define LV1_8_IN_0_OUT_ARGS LV1_8_IN_ARGS
+ #define LV1_0_IN_1_OUT_ARGS                LV1_1_OUT_ARGS
+ #define LV1_1_IN_1_OUT_ARGS LV1_1_IN_ARGS, LV1_1_OUT_ARGS
+@@ -253,6 +255,7 @@
+ LV1_CALL(destruct_logical_spe,                          1, 0,  54 )
+ LV1_CALL(construct_logical_spe,                         7, 6,  57 )
+ LV1_CALL(set_spe_interrupt_mask,                        3, 0,  61 )
++LV1_CALL(undocumented_function_62,                      5, 0,  62 )
+ LV1_CALL(set_spe_transition_notifier,                   3, 0,  64 )
+ LV1_CALL(disable_logical_spe,                           2, 0,  65 )
+ LV1_CALL(clear_spe_interrupt_status,                    4, 0,  66 )
+@@ -269,6 +272,7 @@
+ LV1_CALL(remove_repository_node,                        5, 0,  93 )
+ LV1_CALL(read_htab_entries,                             2, 5,  95 )
+ LV1_CALL(set_dabr,                                      2, 0,  96 )
++LV1_CALL(undocumented_function_99,                      2, 0,  99 )
+ LV1_CALL(get_total_execution_time,                      2, 1, 103 )
+ LV1_CALL(undocumented_function_114,                     3, 1, 114 )
+ LV1_CALL(undocumented_function_115,                     1, 0, 115 )
+@@ -279,12 +283,15 @@
+ LV1_CALL(map_htab,                                      1, 1, 122 )
+ LV1_CALL(unmap_htab,                                    1, 0, 123 )
+ LV1_CALL(get_version_info,                              0, 2, 127 )
++LV1_CALL(undocumented_function_138,                     2, 0, 138 )
+ LV1_CALL(insert_htab_entry,                             6, 3, 158 )
+ LV1_CALL(read_virtual_uart,                             3, 1, 162 )
+ LV1_CALL(write_virtual_uart,                            3, 1, 163 )
+ LV1_CALL(set_virtual_uart_param,                        3, 0, 164 )
+ LV1_CALL(get_virtual_uart_param,                        2, 1, 165 )
+ LV1_CALL(configure_virtual_uart_irq,                    1, 1, 166 )
++LV1_CALL(undocumented_function_167,                     2, 1, 167 )
++LV1_CALL(undocumented_function_168,                     3, 0, 168 )
+ LV1_CALL(open_device,                                   3, 0, 170 )
+ LV1_CALL(close_device,                                  2, 0, 171 )
+ LV1_CALL(map_device_mmio_region,                        5, 1, 172 )
+@@ -305,8 +312,11 @@
+ LV1_CALL(connect_interrupt_event_receive_port,          4, 0, 197 )
+ LV1_CALL(disconnect_interrupt_event_receive_port,       4, 0, 198 )
+ LV1_CALL(get_spe_all_interrupt_statuses,                1, 1, 199 )
++LV1_CALL(undocumented_function_200,                     1, 0, 200 )
++LV1_CALL(undocumented_function_201,                     1, 0, 201 )
+ LV1_CALL(deconfigure_virtual_uart_irq,                  0, 0, 202 )
+ LV1_CALL(enable_logical_spe,                            2, 0, 207 )
++LV1_CALL(undocumented_function_209,                     8, 0, 209 )
+ LV1_CALL(gpu_open,                                      1, 0, 210 )
+ LV1_CALL(gpu_close,                                     0, 0, 211 )
+ LV1_CALL(gpu_device_map,                                1, 2, 212 )
+--- a/arch/powerpc/platforms/ps3/hvcall.S      2012-07-19 22:27:10.499665615 +0200
++++ b/arch/powerpc/platforms/ps3/hvcall.S      2012-07-19 22:27:12.892999087 +0200
+@@ -45,6 +45,7 @@
+ #define LV1_5_IN_0_OUT LV1_N_IN_0_OUT
+ #define LV1_6_IN_0_OUT LV1_N_IN_0_OUT
+ #define LV1_7_IN_0_OUT LV1_N_IN_0_OUT
++#define LV1_8_IN_0_OUT LV1_N_IN_0_OUT
+ #define LV1_0_IN_1_OUT(API_NAME, API_NUMBER)  \
+ _GLOBAL(_##API_NAME)                          \
diff --git a/0190-export-spe-irq-setup-destroy.patch b/0190-export-spe-irq-setup-destroy.patch
new file mode 100644 (file)
index 0000000..2b66825
--- /dev/null
@@ -0,0 +1,18 @@
+--- a/arch/powerpc/platforms/ps3/interrupt.c   2012-06-22 20:37:50.000000000 +0200
++++ b/arch/powerpc/platforms/ps3/interrupt.c   2012-07-15 11:54:14.122392086 +0200
+@@ -608,6 +608,7 @@
+       return result;
+ }
++EXPORT_SYMBOL_GPL(ps3_spe_irq_setup);
+ int ps3_spe_irq_destroy(unsigned int virq)
+ {
+@@ -620,6 +621,7 @@
+       return result;
+ }
++EXPORT_SYMBOL_GPL(ps3_spe_irq_destroy);
+ #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
diff --git a/0200-export-event-receive-port-destroy.patch b/0200-export-event-receive-port-destroy.patch
new file mode 100644 (file)
index 0000000..e1073d1
--- /dev/null
@@ -0,0 +1,10 @@
+--- a/arch/powerpc/platforms/ps3/interrupt.c   2012-07-20 19:47:50.309201716 +0200
++++ b/arch/powerpc/platforms/ps3/interrupt.c   2012-07-20 19:48:29.185870645 +0200
+@@ -381,6 +381,7 @@
+       DBG(" <- %s:%d\n", __func__, __LINE__);
+       return result;
+ }
++EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy);
+ int ps3_send_event_locally(unsigned int virq)
+ {
diff --git a/0210-ps3encdec.patch b/0210-ps3encdec.patch
new file mode 100644 (file)
index 0000000..6944c46
--- /dev/null
@@ -0,0 +1,499 @@
+--- a/arch/powerpc/include/asm/ps3.h   2012-08-02 23:17:17.126972935 +0200
++++ b/arch/powerpc/include/asm/ps3.h   2012-08-06 19:41:56.754901977 +0200
+@@ -328,6 +328,7 @@
+       PS3_MATCH_ID_LPM                = 11,
+       PS3_MATCH_ID_STOR_NOR_FLASH     = 12,
+       PS3_MATCH_ID_DISP_MANAGER       = 13,
++      PS3_MATCH_ID_STOR_ENCDEC        = 14,
+ };
+ enum ps3_match_sub_id {
+@@ -349,6 +350,7 @@
+ #define PS3_MODULE_ALIAS_LPM          "ps3:11:0"
+ #define PS3_MODULE_ALIAS_STOR_NOR_FLASH       "ps3:12:0"
+ #define PS3_MODULE_ALIAS_DISP_MANAGER "ps3:13:0"
++#define PS3_MODULE_ALIAS_STOR_ENCDEC  "ps3:14:0"
+ enum ps3_system_bus_device_type {
+       PS3_DEVICE_TYPE_IOC0 = 1,
+--- a/arch/powerpc/platforms/ps3/platform.h    2012-08-02 23:17:17.110306267 +0200
++++ b/arch/powerpc/platforms/ps3/platform.h    2012-08-06 19:42:30.948237298 +0200
+@@ -89,6 +89,7 @@
+       PS3_DEV_TYPE_SB_GPIO = 6,
+       PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC,     /* 14 */
+       PS3_DEV_TYPE_STOR_NOR_FLASH = 254,
++      PS3_DEV_TYPE_STOR_ENCDEC = 255,
+ };
+ int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
+--- a/arch/powerpc/platforms/ps3/device-init.c 2012-08-02 23:17:17.130306269 +0200
++++ b/arch/powerpc/platforms/ps3/device-init.c 2012-08-06 19:43:12.538239719 +0200
+@@ -621,6 +621,13 @@
+                                __func__, __LINE__);
+               break;
++      case PS3_DEV_TYPE_STOR_ENCDEC:
++              result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ENCDEC);
++              if (result)
++                      pr_debug("%s:%u ps3_setup_storage_dev failed\n",
++                               __func__, __LINE__);
++              break;
++
+       default:
+               result = 0;
+               pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
+--- a/arch/powerpc/platforms/ps3/system-bus.c  2012-08-02 23:17:17.126972935 +0200
++++ b/arch/powerpc/platforms/ps3/system-bus.c  2012-08-06 19:44:03.851576042 +0200
+@@ -175,6 +175,7 @@
+       case PS3_MATCH_ID_STOR_ROM:
+       case PS3_MATCH_ID_STOR_FLASH:
+       case PS3_MATCH_ID_STOR_NOR_FLASH:
++      case PS3_MATCH_ID_STOR_ENCDEC:
+               return ps3_open_hv_device_sb(dev);
+       case PS3_MATCH_ID_SOUND:
+@@ -215,6 +216,7 @@
+       case PS3_MATCH_ID_STOR_ROM:
+       case PS3_MATCH_ID_STOR_FLASH:
+       case PS3_MATCH_ID_STOR_NOR_FLASH:
++      case PS3_MATCH_ID_STOR_ENCDEC:
+               return ps3_close_hv_device_sb(dev);
+       case PS3_MATCH_ID_SOUND:
+--- a/drivers/ps3/ps3stor_lib.c        2012-08-02 23:17:17.100306267 +0200
++++ b/drivers/ps3/ps3stor_lib.c        2012-08-06 20:43:55.981785017 +0200
+@@ -90,8 +90,9 @@
+       unsigned int i;
+       unsigned long n;
+-      if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) {
+-              /* special case: CD-ROM is assumed always accessible */
++      if ((dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) ||
++          (dev->sbd.match_id == PS3_MATCH_ID_STOR_ENCDEC)) {
++              /* special case: CD-ROM and ENCDEC are assumed always accessible */
+               dev->accessible_regions = 1;
+               return 0;
+       }
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-08-02 23:17:17.153639603 +0200
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-08-06 19:46:13.304916903 +0200
+@@ -192,6 +192,16 @@
+         This driver allows you to create/delete/modify regions
+         on PS3 storage devices.
++config PS3_ENCDEC
++      tristate "PS3 ENCDEC Driver"
++      depends on PPC_PS3
++      select PS3_STORAGE
++      help
++        Include support for the PS3 ENCDEC device.
++
++        This support is required to access the PS3 ENCDEC device.
++        In general, all users will say Y or M.
++
+ config PS3GELIC_UDBG
+       bool "PS3 udbg output via UDP broadcasts on Ethernet"
+       depends on PPC_PS3
+--- a/drivers/char/Makefile    2012-08-02 23:17:17.153639603 +0200
++++ b/drivers/char/Makefile    2012-08-06 19:47:18.001587333 +0200
+@@ -67,3 +67,4 @@
+ obj-$(CONFIG_PS3_PHYSMEM)     += ps3physmem.o
+ obj-$(CONFIG_PS3_STRGMNGR)    += ps3strgmngr.o
++obj-$(CONFIG_PS3_ENCDEC)      += ps3encdec.o
+--- /dev/null  2012-08-07 02:54:53.492474007 +0200
++++ b/drivers/char/ps3encdec.c 2012-08-07 02:56:38.822480157 +0200
+@@ -0,0 +1,394 @@
++/*
++ * PS3 ENCDEC Driver
++ *
++ * Copyright (C) 2011 graf_chokolo <[email protected]>
++ * Copyright (C) 2011, 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/sched.h>
++#include <linux/mutex.h>
++#include <linux/poll.h>
++#include <linux/uaccess.h>
++#include <linux/compat.h>
++#include <linux/miscdevice.h>
++
++#include <asm/ps3.h>
++#include <asm/lv1call.h>
++#include <asm/ps3stor.h>
++#include <asm/firmware.h>
++
++#define DEVICE_NAME           "ps3encdec"
++
++#define BOUNCE_SIZE           (4 * 1024)
++
++struct ps3encdec_private
++{
++      struct ps3_storage_device *dev;
++      struct miscdevice misc;
++      char *bounce_wbuf;
++      u64 bounce_wlpar;
++      char *bounce_rbuf;
++      u64 bounce_rlpar;
++      struct mutex mtx;
++      wait_queue_head_t read_wq;
++      wait_queue_head_t write_wq;
++      int cmd_done;
++      int cmd_failed;
++      int cmd_data_avail;
++};
++
++static struct ps3encdec_private *ps3encdec_priv;
++
++static ssize_t ps3encdec_read(struct file *file, char __user *usrbuf,
++      size_t count, loff_t *pos)
++{
++      struct ps3encdec_private *priv = ps3encdec_priv;
++      int res = 0;
++
++      if (mutex_lock_interruptible(&priv->mtx))
++              return (-ERESTARTSYS);
++
++      if (file->f_flags & O_NONBLOCK) {
++              if (!priv->cmd_done || priv->cmd_failed)
++                      res = -EAGAIN;
++      } else {
++              DEFINE_WAIT(__wait);
++
++              while (1) {
++                      prepare_to_wait(&priv->read_wq, &__wait, TASK_INTERRUPTIBLE);
++
++                      if (priv->cmd_data_avail)
++                              break;
++      
++                      mutex_unlock(&priv->mtx);
++
++                      if (signal_pending(current)) {
++                              finish_wait(&priv->read_wq, &__wait);
++                              return (-ERESTARTSYS);
++                      }
++
++                      schedule();
++
++                      res = mutex_lock_interruptible(&priv->mtx);
++                      if (res) {
++                              finish_wait(&priv->read_wq, &__wait);
++                              return (res);
++                      }
++              }
++
++              finish_wait(&priv->read_wq, &__wait);
++      }
++
++      if (res)
++              goto done;
++
++      if (count > BOUNCE_SIZE)
++              count = BOUNCE_SIZE;
++
++      if (!count || (priv->cmd_done && priv->cmd_failed))
++              goto done;
++
++      if (copy_to_user(usrbuf, priv->bounce_rbuf + *pos, count)) {
++              res = -EFAULT;
++              goto done;
++      }
++
++      priv->cmd_data_avail = 0;
++
++      res = count;
++
++done:
++
++      mutex_unlock(&priv->mtx);
++
++      return (res);
++}
++
++static ssize_t ps3encdec_write(struct file *file, const char __user *usrbuf,
++      size_t count, loff_t *pos)
++{
++      struct ps3encdec_private *priv = ps3encdec_priv;
++      struct ps3_storage_device *dev = priv->dev;
++      u32 cmd;
++      int res = 0;
++
++      if (mutex_lock_interruptible(&priv->mtx))
++              return (-ERESTARTSYS);
++
++      if (file->f_flags & O_NONBLOCK) {
++              if (!priv->cmd_done)
++                      res = -EAGAIN;
++      } else {
++              DEFINE_WAIT(__wait);
++
++              while (1) {
++                      prepare_to_wait(&priv->write_wq, &__wait, TASK_INTERRUPTIBLE);
++
++                      if (priv->cmd_done)
++                              break;
++      
++                      mutex_unlock(&priv->mtx);
++
++                      if (signal_pending(current)) {
++                              finish_wait(&priv->write_wq, &__wait);
++                              return (-ERESTARTSYS);
++                      }
++
++                      schedule();
++
++                      res = mutex_lock_interruptible(&priv->mtx);
++                      if (res) {
++                              finish_wait(&priv->write_wq, &__wait);
++                              return (res);
++                      }
++              }
++
++              finish_wait(&priv->write_wq, &__wait);
++      }
++
++      if (res)
++              goto done;
++
++      if (count > BOUNCE_SIZE + sizeof(cmd))
++              count = BOUNCE_SIZE + sizeof(cmd);
++
++      if (!count)
++              goto done;
++
++      if (count < sizeof(cmd)) {
++              res = -EINVAL;
++              goto done;
++      }
++
++      if (copy_from_user(&cmd, usrbuf, sizeof(cmd))) {
++              res = -EFAULT;
++              goto done;
++      }
++
++      if (copy_from_user(priv->bounce_wbuf, usrbuf + sizeof(cmd), count - sizeof(cmd))) {
++              res = -EFAULT;
++              goto done;
++      }
++
++      priv->cmd_done = 0;
++      priv->cmd_failed = 1;
++      priv->cmd_data_avail = 0;
++
++      res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd,
++              priv->bounce_wlpar, count - sizeof(cmd),
++              priv->bounce_rlpar, BOUNCE_SIZE, &dev->tag);
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: res=%d\n",
++                      __func__, __LINE__, res);
++              priv->cmd_done = 1;
++              res = -EIO;
++              goto done;
++      }
++
++      res = count;
++
++done:
++
++      mutex_unlock(&priv->mtx);
++
++      return (res);
++}
++
++static unsigned int ps3encdec_poll(struct file *file, poll_table *wait)
++{
++      struct ps3encdec_private *priv = ps3encdec_priv;
++      unsigned int mask = 0;
++
++      mutex_lock(&priv->mtx);
++
++      poll_wait(file, &priv->read_wq, wait);
++      poll_wait(file, &priv->write_wq, wait);
++
++      if (priv->cmd_data_avail)
++              mask |= POLLIN | POLLRDNORM;
++
++      if (priv->cmd_done)
++              mask |= POLLOUT | POLLWRNORM;
++
++      mutex_unlock(&priv->mtx);
++
++      return (mask);
++}
++
++static irqreturn_t ps3encdec_interrupt(int irq, void *data)
++{
++      struct ps3_storage_device *dev = data;
++      struct ps3encdec_private *priv;
++      u64 tag, status;
++      int res;
++
++      res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
++
++      pr_info("%s:%d: res=%d status=%llx\n", __func__, __LINE__, res, status);
++
++      if (tag != dev->tag) {
++              dev_err(&dev->sbd.core,
++                      "%s:%u: tag mismatch, got %llx, expected %llx\n",
++                      __func__, __LINE__, tag, dev->tag);
++      }
++
++      if (res) {
++              dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
++                      __func__, __LINE__, res, status);
++              return (IRQ_HANDLED);
++      }
++
++      priv = ps3_system_bus_get_drvdata(&dev->sbd);
++
++      priv->cmd_done = 1;
++      priv->cmd_failed = (status != 0);
++      priv->cmd_data_avail = !priv->cmd_failed;
++
++      wake_up_interruptible(&priv->read_wq);
++      wake_up_interruptible(&priv->write_wq);
++
++      return (IRQ_HANDLED);
++}
++
++static const struct file_operations ps3encdec_fops = {
++      .owner = THIS_MODULE,
++      .open = nonseekable_open,
++      .read = ps3encdec_read,
++      .write = ps3encdec_write,
++      .poll = ps3encdec_poll,
++};
++
++static int ps3encdec_probe(struct ps3_system_bus_device *_dev)
++{
++      struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
++      struct ps3encdec_private *priv;
++      int res;
++
++      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return (-ENOMEM);
++
++      ps3_system_bus_set_drvdata(_dev, priv);
++
++      dev->bounce_size = BOUNCE_SIZE * 2;
++      dev->bounce_buf = kmalloc(dev->bounce_size, GFP_DMA);
++      if (!dev->bounce_buf) {
++              res = -ENOMEM;
++              goto fail_free_priv;
++      }
++
++      res = ps3stor_setup(dev, ps3encdec_interrupt);
++      if (res)
++              goto fail_free_bounce;
++
++      mutex_init(&priv->mtx);
++
++      init_waitqueue_head(&priv->read_wq);
++      init_waitqueue_head(&priv->write_wq);
++
++      priv->cmd_done = 1;
++      priv->cmd_failed = 0;
++      priv->cmd_data_avail = 0;
++
++      priv->misc.minor = MISC_DYNAMIC_MINOR,
++      priv->misc.name = DEVICE_NAME,
++      priv->misc.fops = &ps3encdec_fops,
++
++      res = misc_register(&priv->misc);
++      if (res)
++              goto fail_teardown;
++
++      priv->dev = dev;
++      priv->bounce_wbuf = dev->bounce_buf;
++      priv->bounce_wlpar = dev->bounce_lpar;
++      priv->bounce_rbuf = dev->bounce_buf + BOUNCE_SIZE;
++      priv->bounce_rlpar = dev->bounce_lpar + BOUNCE_SIZE;
++
++      ps3encdec_priv = priv;
++
++      return (0);
++
++fail_teardown:
++
++      ps3stor_teardown(dev);
++
++fail_free_bounce:
++
++      kfree(dev->bounce_buf);
++
++fail_free_priv:
++
++      kfree(priv);
++      ps3_system_bus_set_drvdata(_dev, NULL);
++
++      return (res);
++}
++
++static int ps3encdec_remove(struct ps3_system_bus_device *_dev)
++{
++      struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
++      struct ps3encdec_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
++
++      ps3encdec_priv = NULL;
++
++      misc_deregister(&priv->misc);
++      ps3stor_teardown(dev);
++      kfree(dev->bounce_buf);
++      kfree(priv);
++      ps3_system_bus_set_drvdata(_dev, NULL);
++
++      return (0);
++}
++
++static struct ps3_system_bus_driver ps3encdec = {
++      .match_id       = PS3_MATCH_ID_STOR_ENCDEC,
++      .core.name      = DEVICE_NAME,
++      .core.owner     = THIS_MODULE,
++      .probe          = ps3encdec_probe,
++      .remove         = ps3encdec_remove,
++      .shutdown       = ps3encdec_remove,
++};
++
++static int __init ps3encdec_init(void)
++{
++      int res;
++
++      if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
++              return (-ENODEV);
++
++      res = ps3_system_bus_driver_register(&ps3encdec);
++
++      return (res);
++}
++
++static void __exit ps3encdec_exit(void)
++{
++      ps3_system_bus_driver_unregister(&ps3encdec);
++}
++
++module_init(ps3encdec_init);
++module_exit(ps3encdec_exit);
++
++MODULE_AUTHOR("glevand");
++MODULE_DESCRIPTION("PS3 ENCDEC Driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_ENCDEC);
diff --git a/0220-spuisofs.patch b/0220-spuisofs.patch
new file mode 100644 (file)
index 0000000..1cabe96
--- /dev/null
@@ -0,0 +1,1095 @@
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-08-08 21:45:40.869511244 +0200
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-08-08 21:43:09.502835770 +0200
+@@ -202,6 +202,13 @@
+         This support is required to access the PS3 ENCDEC device.
+         In general, all users will say Y or M.
++config SPUISO_FS
++      tristate "PS3 isolated SPU file system"
++      default m
++      depends on PPC_PS3
++      help
++        The isolated SPU file system is used to execute isolated SPU modules.
++
+ config PS3GELIC_UDBG
+       bool "PS3 udbg output via UDP broadcasts on Ethernet"
+       depends on PPC_PS3
+--- a/arch/powerpc/platforms/ps3/Makefile      2012-08-08 21:45:25.436177008 +0200
++++ b/arch/powerpc/platforms/ps3/Makefile      2012-08-08 21:43:40.846170928 +0200
+@@ -6,3 +6,5 @@
+ obj-$(CONFIG_SMP) += smp.o
+ obj-$(CONFIG_SPU_BASE) += spu.o
+ obj-y += device-init.o
++
++obj-$(CONFIG_SPUISO_FS) += spuisofs.o
+--- /dev/null  2013-10-07 20:20:12.741358433 +0200
++++ b/arch/powerpc/platforms/ps3/spuisofs.c    2013-10-07 22:15:48.445095266 +0200
+@@ -0,0 +1,1068 @@
++
++/*
++ * PS3 spuisofs
++ *
++ * Copyright (C) 2012-2013 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/fsnotify.h>
++#include <linux/file.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/pagemap.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++
++#include <asm/uaccess.h>
++#include <asm/ps3.h>
++#include <asm/spu.h>
++#include <asm/spu_priv1.h>
++#include <asm/lv1call.h>
++
++#define SPUISOFS_MAGIC                0x73707569
++
++struct spe_shadow {
++      u8 padding_0140[0x0140];
++      u64 int_status_class0_RW;       /* 0x0140 */
++      u64 int_status_class1_RW;       /* 0x0148 */
++      u64 int_status_class2_RW;       /* 0x0150 */
++      u8 padding_0158[0x0610-0x0158];
++      u64 mfc_dsisr_RW;               /* 0x0610 */
++      u8 padding_0618[0x0620-0x0618];
++      u64 mfc_dar_RW;                 /* 0x0620 */
++      u8 padding_0628[0x0800-0x0628];
++      u64 mfc_dsipr_R;                /* 0x0800 */
++      u8 padding_0808[0x0810-0x0808];
++      u64 mfc_lscrr_R;                /* 0x0810 */
++      u8 padding_0818[0x0c00-0x0818];
++      u64 mfc_cer_R;                  /* 0x0c00 */
++      u8 padding_0c08[0x0f00-0x0c08];
++      u64 spe_execution_status;       /* 0x0f00 */
++      u8 padding_0f08[0x1000-0x0f08];
++};
++
++struct spuisofs_inode_info {
++      struct inode vfs_inode;
++      unsigned long io_addr;
++      void *virt_addr;
++};
++
++struct spuisofs_tree_descr {
++      const char *name;
++      const struct file_operations *ops;
++      umode_t mode;
++      size_t size;
++      unsigned long io_addr;
++      void *virt_addr;
++};
++
++#define SPUISOFS_I(inode)     container_of(inode, struct spuisofs_inode_info, vfs_inode)
++
++struct spuisofs_run_args {
++      u64 laid;
++      u64 arg1_size;
++      u64 arg2_size;
++};
++
++static struct kmem_cache *spuisofs_inode_cache;
++
++static u64 spuisofs_spe_priv2_addr;
++static u64 spuisofs_spe_problem_addr;
++static u64 spuisofs_spe_ls_addr;
++static u64 spuisofs_spe_shadow_addr;
++
++static void *spuisofs_spe_priv2;
++static struct spu_problem *spuisofs_spe_problem;
++static void *spuisofs_spe_ls;
++static struct spe_shadow *spuisofs_spe_shadow;
++static u64 spuisofs_spe_id;
++static unsigned int spuisofs_spe_virq[4];
++
++static void *spuisofs_spe_app;
++static void *spuisofs_spe_arg1;
++static void *spuisofs_spe_arg2;
++static void *spuisofs_spe_buf;
++
++static unsigned int spuisofs_spe_slb_index;
++
++static unsigned long spuisofs_spe_app_size = 1024 * 1024;
++module_param(spuisofs_spe_app_size, ulong, 0);
++
++static unsigned long spuisofs_spe_arg1_size = 1024 * 1024;
++module_param(spuisofs_spe_arg1_size, ulong, 0);
++
++static unsigned long spuisofs_spe_arg2_size = 1024 * 1024;
++module_param(spuisofs_spe_arg2_size, ulong, 0);
++
++static unsigned long spuisofs_spe_buf_size = 1024 * 1024;
++module_param(spuisofs_spe_buf_size, ulong, 0);
++
++static unsigned long spuisofs_spe_trans_notify_mask = 0xf;
++module_param(spuisofs_spe_trans_notify_mask, ulong, 0);
++
++static unsigned long spuisofs_spe_resource_id = 6;
++module_param(spuisofs_spe_resource_id, ulong, 0);
++
++static int spuisofs_spe_buf_addr_32bit = 0;
++module_param(spuisofs_spe_buf_addr_32bit, int, 0);
++
++/*
++ * spuisofs_spe_regs_read
++ */
++static ssize_t spuisofs_spe_regs_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (0);
++
++      return simple_read_from_buffer(buffer, size, pos,
++              si->virt_addr, inode->i_size);
++}
++
++/*
++ * spuisofs_spe_regs_write
++ */
++static ssize_t spuisofs_spe_regs_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (-EFBIG);
++
++      return simple_write_to_buffer(si->virt_addr, inode->i_size,
++              pos, buffer, size);
++}
++
++/*
++ * spuisofs_spe_regs_mmap
++ */
++static int spuisofs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++      unsigned long size, pfn;
++
++      size = vma->vm_end - vma->vm_start;
++      pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
++      vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
++#else
++      vma->vm_flags |= VM_RESERVED | VM_IO;
++#endif
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++      return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
++}
++
++static const struct file_operations spuisofs_spe_regs_fops = {
++      .read   = spuisofs_spe_regs_read,
++      .write  = spuisofs_spe_regs_write,
++      .mmap   = spuisofs_spe_regs_mmap,
++};
++
++/*
++ * spuisofs_spe_mem_read
++ */
++static ssize_t spuisofs_spe_mem_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (0);
++
++      return simple_read_from_buffer(buffer, size, pos,
++              si->virt_addr, inode->i_size);
++}
++
++/*
++ * spuisofs_spe_mem_write
++ */
++static ssize_t spuisofs_spe_mem_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (-EFBIG);
++
++      return simple_write_to_buffer(si->virt_addr, inode->i_size,
++              pos, buffer, size);
++}
++
++/*
++ * spuisofs_spe_mem_mmap
++ */
++static int spuisofs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++      unsigned long size, pfn;
++
++      size = vma->vm_end - vma->vm_start;
++      pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
++      vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
++#else
++      vma->vm_flags |= VM_RESERVED | VM_IO;
++#endif
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++      return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
++}
++
++static const struct file_operations spuisofs_spe_mem_fops = {
++      .read   = spuisofs_spe_mem_read,
++      .write  = spuisofs_spe_mem_write,
++      .mmap   = spuisofs_spe_mem_mmap,
++};
++
++/*
++ * spuisofs_mem_read
++ */
++static ssize_t spuisofs_mem_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (0);
++
++      return simple_read_from_buffer(buffer, size, pos,
++              si->virt_addr, inode->i_size);
++}
++
++/*
++ * spuisofs_mem_write
++ */
++static ssize_t spuisofs_mem_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (-EFBIG);
++
++      return simple_write_to_buffer(si->virt_addr, inode->i_size,
++              pos, buffer, size);
++}
++
++/*
++ * spuisofs_mem_mmap
++ */
++static int spuisofs_mem_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_inode_info *si = SPUISOFS_I(inode);
++
++      return remap_vmalloc_range(vma, si->virt_addr, 0);
++}
++
++static const struct file_operations spuisofs_mem_fops = {
++      .read   = spuisofs_mem_read,
++      .write  = spuisofs_mem_write,
++      .mmap   = spuisofs_mem_mmap,
++};
++
++/*
++ * spuisofs_info_read
++ */
++static ssize_t spuisofs_info_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      char buf[256];
++      size_t len;
++      unsigned long spe_buf_addr;
++
++      spe_buf_addr = (unsigned long) spuisofs_spe_buf;
++
++      if (spuisofs_spe_buf_addr_32bit)
++              spe_buf_addr &= 0xfffffffful;
++
++      len = sprintf(buf, "arg1 %lx\narg2 %lx\nbuf %lx\n",
++              (unsigned long) spuisofs_spe_arg1, (unsigned long) spuisofs_spe_arg2,
++              spe_buf_addr);
++
++      return simple_read_from_buffer(buffer, size, pos, buf, len);
++}
++
++static const struct file_operations spuisofs_info_fops = {
++      .read   = spuisofs_info_read,
++};
++
++/*
++ * spuisofs_run_write
++ */
++static ssize_t spuisofs_run_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuisofs_run_args args;
++      int err;
++
++      if (*pos || (size != inode->i_size))
++              return (-EINVAL);
++
++      if (copy_from_user(&args, buffer, sizeof(struct spuisofs_run_args)))
++              return -EFAULT;
++
++      if (args.arg1_size == 0)
++              args.arg1_size = spuisofs_spe_arg1_size;
++
++      if (args.arg2_size == 0)
++              args.arg2_size = spuisofs_spe_arg2_size;
++
++      if (args.arg1_size > spuisofs_spe_arg1_size)
++              return (-EINVAL);
++
++      if (args.arg2_size > spuisofs_spe_arg2_size)
++              return (-EINVAL);
++
++      err = lv1_undocumented_function_201(spuisofs_spe_id);
++      if (err)
++              printk(KERN_INFO"spuisofs: lv1_undocumented_function_201 failed with %d\n", err);
++
++      err = lv1_undocumented_function_209(spuisofs_spe_id, args.laid, (u64) spuisofs_spe_app,
++              (u64) spuisofs_spe_arg1, args.arg1_size,
++              (u64) spuisofs_spe_arg2, args.arg2_size, spuisofs_spe_resource_id);
++      if (err) {
++              printk(KERN_INFO"spuisofs: lv1_undocumented_function_209 failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      return (size);
++}
++
++static const struct file_operations spuisofs_run_fops = {
++      .write  = spuisofs_run_write,
++};
++
++/*
++ * spuisofs_cont_write
++ */
++static ssize_t spuisofs_cont_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      u64 puint_mb_R;
++      int err;
++
++      err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R);
++      if (err) {
++              printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R);
++
++      err = lv1_undocumented_function_200(spuisofs_spe_id);
++      if (err) {
++              printk(KERN_INFO"spuisofs: lv1_undocumented_function_200 failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      return (size);
++}
++
++static const struct file_operations spuisofs_cont_fops = {
++      .write  = spuisofs_cont_write,
++};
++
++/*
++ * spuisofs_alloc_inode
++ */
++static struct inode *spuisofs_alloc_inode(struct super_block *sb)
++{
++      struct spuisofs_inode_info *si;
++
++      si = kmem_cache_alloc(spuisofs_inode_cache, GFP_KERNEL);
++      if (!si)
++              return (NULL);
++
++      return (&si->vfs_inode);
++}
++ 
++/*
++ * spuisofs_i_callback
++ */
++static void spuisofs_i_callback(struct rcu_head *head)
++{
++      struct inode *inode = container_of(head, struct inode, i_rcu);
++
++      kmem_cache_free(spuisofs_inode_cache, SPUISOFS_I(inode));
++}
++
++/*
++ * spuisofs_destroy_inode
++ */
++static void spuisofs_destroy_inode(struct inode *inode)
++{
++      call_rcu(&inode->i_rcu, spuisofs_i_callback);
++}
++
++/*
++ * spuisofs_init_once
++ */
++static void spuisofs_init_once(void *p)
++{
++      struct spuisofs_inode_info *si = p;
++
++      inode_init_once(&si->vfs_inode);
++}
++
++/*
++ * spuisofs_new_inode
++ */
++static struct inode *spuisofs_new_inode(struct super_block *sb, umode_t mode)
++{
++      struct inode *inode;
++      
++      inode = new_inode(sb);
++      if (!inode)
++              return (NULL);
++      
++      inode->i_ino = get_next_ino();
++      inode->i_mode = mode;
++      inode->i_uid = current_fsuid();
++      inode->i_gid = current_fsgid();
++      inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
++
++      return (inode);
++}
++
++/*
++ * spuisofs_setattr
++ */
++static int spuisofs_setattr(struct dentry *dentry, struct iattr *attr)
++{
++      struct inode *inode = dentry->d_inode;
++
++      setattr_copy(inode, attr);
++      mark_inode_dirty(inode);
++
++      return (0);
++}
++
++static const struct inode_operations spuisofs_inode_ops = {
++      .setattr = spuisofs_setattr,
++};
++
++/*
++ * spuisofs_new_file
++ */
++static int spuisofs_new_file(struct super_block *sb, struct dentry *dentry,
++      const struct file_operations *fops, umode_t mode, size_t size,
++      unsigned long io_addr, void *virt_addr)
++{
++      struct inode *inode;
++      struct spuisofs_inode_info *si;
++
++      inode = spuisofs_new_inode(sb, S_IFREG | mode);
++      if (!inode)
++              return (-ENOMEM);
++
++      inode->i_op = &spuisofs_inode_ops;
++      inode->i_fop = fops;
++      inode->i_size = size;
++      inode->i_private = NULL;
++
++      si = SPUISOFS_I(inode);
++      si->io_addr = io_addr;
++      si->virt_addr = virt_addr;
++
++      d_add(dentry, inode);
++
++      return (0);
++}
++
++/*
++ * spuisofs_fill_dir
++ */
++static int spuisofs_fill_dir(struct dentry *dir,
++      const struct spuisofs_tree_descr *files)
++{
++      struct dentry *dentry, *tmp;
++      int err;
++
++      while (files->name && files->name[0]) {
++              dentry = d_alloc_name(dir, files->name);
++              if (!dentry) {
++                      err = -ENOMEM;
++                      goto fail;
++              }
++
++              err = spuisofs_new_file(dir->d_sb, dentry, files->ops,
++                      files->mode, files->size, files->io_addr, files->virt_addr);
++              if (err)
++                      goto fail;
++
++              files++;
++      }
++
++      return (0);
++
++fail:
++
++      list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child)
++              dput(dentry);
++
++      shrink_dcache_parent(dir);
++
++      return (err);
++}
++
++static const struct super_operations spuisofs_super_ops = {
++      .alloc_inode    = spuisofs_alloc_inode,
++      .destroy_inode  = spuisofs_destroy_inode,
++      .statfs         = simple_statfs,
++};
++
++/*
++ * spuisofs_fill_super
++ */
++static int spuisofs_fill_super(struct super_block *sb, void *data, int silent)
++{
++      const struct spuisofs_tree_descr root_dir_contents[] = {
++              { "priv2", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuisofs_spe_priv2_addr, spuisofs_spe_priv2, },
++              { "problem", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuisofs_spe_problem_addr, spuisofs_spe_problem, },
++              { "ls", &spuisofs_spe_mem_fops, 0666, LS_SIZE, spuisofs_spe_ls_addr, spuisofs_spe_ls, },
++              { "shadow", &spuisofs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuisofs_spe_shadow_addr, spuisofs_spe_shadow, },
++              { "app", &spuisofs_mem_fops, 0666, spuisofs_spe_app_size, 0, spuisofs_spe_app, },
++              { "arg1", &spuisofs_mem_fops, 0666, spuisofs_spe_arg1_size, 0, spuisofs_spe_arg1, },
++              { "arg2", &spuisofs_mem_fops, 0666, spuisofs_spe_arg2_size, 0, spuisofs_spe_arg2, },
++              { "buf", &spuisofs_mem_fops, 0666, spuisofs_spe_buf_size, 0, spuisofs_spe_buf, },
++              { "info", &spuisofs_info_fops, 0444, 0, 0, NULL, },
++              { "run", &spuisofs_run_fops, 0222, sizeof(struct spuisofs_run_args), 0, NULL, },
++              { "cont", &spuisofs_cont_fops, 0222, 0, 0, NULL, },
++              { },
++      };
++      struct inode *root_inode;
++      int err;
++ 
++      sb->s_maxbytes = MAX_LFS_FILESIZE;
++      sb->s_blocksize = PAGE_CACHE_SIZE;
++      sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
++      sb->s_magic = SPUISOFS_MAGIC;
++      sb->s_op = &spuisofs_super_ops;
++      sb->s_time_gran = 1;
++ 
++      root_inode = spuisofs_new_inode(sb, S_IFDIR | 0755);
++      if (!root_inode)
++              return (-ENOMEM);
++
++      root_inode->i_op = &simple_dir_inode_operations;
++      root_inode->i_fop = &simple_dir_operations;
++
++      /* directory inodes start off with i_nlink == 2 (for "." entry) */
++      inc_nlink(root_inode);
++
++      sb->s_root = d_make_root(root_inode);
++      if (!sb->s_root)
++              return (-ENOMEM);
++
++      err = spuisofs_fill_dir(sb->s_root, root_dir_contents);
++      if (err)
++              return (err);
++      
++      return (0);
++}
++
++/*
++ * spuisofs_spe_ea_to_kernel_ea
++ */
++static unsigned long spuisofs_spe_ea_to_kernel_ea(unsigned long spe_ea)
++{
++      unsigned long kernel_ea, spe_buf_addr;
++
++      kernel_ea = spe_ea;
++
++      if (!spuisofs_spe_buf_addr_32bit)
++              return (kernel_ea);
++
++      spe_buf_addr = (unsigned long) spuisofs_spe_buf & 0xfffffffful;
++
++      if ((spe_ea >= spe_buf_addr) && (spe_ea < (spe_buf_addr + spuisofs_spe_buf_size)))
++              kernel_ea = (unsigned long) spuisofs_spe_buf + (spe_buf_addr - spe_ea);
++
++      return (kernel_ea);
++}
++
++/*
++ * spuisofs_spe_interrupt
++ */
++static irqreturn_t spuisofs_spe_interrupt(int irq, void *data)
++{
++      u64 status;
++      u64 ea, kernel_ea, dsisr, esid, vsid;
++      u64 puint_mb_R;
++      u32 spu_status_R;
++      u64 spe_execution_status;
++      int err;
++
++      if (irq == spuisofs_spe_virq[0]) {
++              printk(KERN_INFO"spuisofs: got class 0 irq\n");
++
++              err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 0, &status);
++              if (err) {
++                      printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              printk(KERN_INFO"spuisofs: status %llx\n", status);
++
++              err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 0, status, 0);
++              if (err) {
++                      printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++      } else if (irq == spuisofs_spe_virq[1]) {
++              printk(KERN_INFO"spuisofs: got class 1 irq\n");
++
++              err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 1, &status);
++              if (err) {
++                      printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              printk(KERN_INFO"spuisofs: status %llx\n", status);
++
++              if (status & CLASS1_SEGMENT_FAULT_INTR) {
++                      ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW);
++                      kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea);
++
++                      esid = (ea & ESID_MASK) | SLB_ESID_V;
++                      vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K;
++
++                      printk(KERN_INFO"spuisofs: data segment fault at %llx (%llx)\n", ea, kernel_ea);
++
++                      err = lv1_undocumented_function_62(spuisofs_spe_id, 0, spuisofs_spe_slb_index, esid, vsid);
++                      if (err) {
++                              printk(KERN_INFO"spuisofs: lv1_undocumented_function_62 failed with %d\n", err);
++                              goto out;
++                      }
++
++                      spuisofs_spe_slb_index++;
++                      if (spuisofs_spe_slb_index > SLB_INDEX_MASK)
++                              spuisofs_spe_slb_index = 0;
++              }
++
++              if (status & CLASS1_STORAGE_FAULT_INTR) {
++                      ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW);
++                      kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea);
++                      dsisr = in_be64(&spuisofs_spe_shadow->mfc_dsisr_RW);
++
++                      printk(KERN_INFO"spuisofs: data storage fault at %llx (%llx)\n", ea, kernel_ea);
++
++                      if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
++                              err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300);
++                              if (err) {
++                                      printk(KERN_INFO"spuisofs: hash_page failed with %d\n", err);
++                                      goto out;
++                              }
++                      }
++              }
++
++              err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 1, status, 0);
++              if (err) {
++                      printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              /* restart DMA */
++
++              err = lv1_undocumented_function_168(spuisofs_spe_id, 0x3000, 1ull << 32);
++              if (err) {
++                      printk(KERN_INFO"spuisofs: lv1_undocumented_function_168 failed with %d\n", err);
++                      goto out;
++              }
++      } else if (irq == spuisofs_spe_virq[2]) {
++              printk(KERN_INFO"spuisofs: got class 2 irq\n");
++
++              err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 2, &status);
++              if (err) {
++                      printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              printk(KERN_INFO"spuisofs: status %llx\n", status);
++
++              if (status & CLASS2_MAILBOX_INTR) {
++                      err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R);
++                      if (err) {
++                              printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err);
++                              goto out;
++                      }
++
++                      printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R);
++              }
++
++              if (status & CLASS2_SPU_STOP_INTR) {
++                      spu_status_R = in_be32(&spuisofs_spe_problem->spu_status_R);
++
++                      printk(KERN_INFO"spuisofs: spu_status_R %x\n", spu_status_R);
++              }
++
++              err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 2, status, 0);
++              if (err) {
++                      printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++      } else if (irq == spuisofs_spe_virq[3]) {
++              spe_execution_status = spuisofs_spe_shadow->spe_execution_status;
++
++              printk(KERN_INFO"spuisofs: transition notification: shadow spe_execution_status %llx\n",
++                      spe_execution_status);
++      } else {
++              printk(KERN_INFO"spuisofs: got unknown irq %d\n", irq);
++      }
++
++out:
++
++      return (IRQ_HANDLED);
++}
++
++/*
++ * spuisofs_create_spe
++ */
++static int spuisofs_create_spe(void)
++{
++      u64 vas_id, junk;
++      int err;
++
++      err = lv1_get_virtual_address_space_id_of_ppe(&vas_id);
++      if (err)
++              return (-ENXIO);
++
++      printk(KERN_INFO"spuisofs: vas id %llu\n", vas_id);
++
++      err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT,
++              PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 2,
++              &spuisofs_spe_priv2_addr, &spuisofs_spe_problem_addr, &spuisofs_spe_ls_addr,
++              &junk, &spuisofs_spe_shadow_addr, &spuisofs_spe_id);
++      if (err)
++              return (-ENXIO);
++
++      printk(KERN_INFO"spuisofs: spe id %llu\n", spuisofs_spe_id);
++
++      spuisofs_spe_priv2 = ioremap(spuisofs_spe_priv2_addr, sizeof(struct spu_priv2));
++      if (!spuisofs_spe_priv2) {
++              err = -ENOMEM;
++              goto fail_destruct_spe;
++      }
++
++      spuisofs_spe_problem = ioremap(spuisofs_spe_problem_addr, sizeof(struct spu_problem));
++      if (!spuisofs_spe_problem) {
++              err = -ENOMEM;
++              goto fail_unmap_priv2;
++      }
++
++      spuisofs_spe_ls = ioremap_prot(spuisofs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE);
++      if (!spuisofs_spe_ls) {
++              err = -ENOMEM;
++              goto fail_unmap_problem;
++      }
++
++      spuisofs_spe_shadow = __ioremap(spuisofs_spe_shadow_addr, sizeof(struct spe_shadow),
++              _PAGE_NO_CACHE | 3);
++      if (!spuisofs_spe_shadow) {
++              err = -ENOMEM;
++              goto fail_unmap_ls;
++      }
++
++      err =  ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 0, &spuisofs_spe_virq[0]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_unmap_shadow;
++      }
++
++      err = request_irq(spuisofs_spe_virq[0], spuisofs_spe_interrupt, 0,
++              "spuisofs_spe_irq0", &spuisofs_spe_virq[0]);
++      if (err)
++              goto fail_destroy_spe_irq_0;
++
++      err =  ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 1, &spuisofs_spe_virq[1]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_free_spe_irq_0;
++      }
++
++      err = request_irq(spuisofs_spe_virq[1], spuisofs_spe_interrupt, 0,
++              "spuisofs_spe_irq1", &spuisofs_spe_virq[1]);
++      if (err)
++              goto fail_destroy_spe_irq_1;
++
++      err =  ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 2, &spuisofs_spe_virq[2]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_free_spe_irq_1;
++      }
++
++      err = request_irq(spuisofs_spe_virq[2], spuisofs_spe_interrupt, 0,
++              "spuisofs_spe_irq2", &spuisofs_spe_virq[2]);
++      if (err)
++              goto fail_destroy_spe_irq_2;
++
++      err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuisofs_spe_virq[3]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_free_spe_irq_2;
++      }
++
++      err = lv1_set_spe_transition_notifier(spuisofs_spe_id, spuisofs_spe_trans_notify_mask,
++              virq_to_hw(spuisofs_spe_virq[3]));
++      if (err) {
++              printk(KERN_INFO"spuisofs: lv1_set_spe_transition_notifier failed with %d\n", err);
++              err = -ENXIO;
++              goto fail_destroy_event_recv_port;
++      }
++
++      err = request_irq(spuisofs_spe_virq[3], spuisofs_spe_interrupt, 0,
++              "spuisofs_spe_irq3", &spuisofs_spe_virq[3]);
++      if (err)
++              goto fail_destroy_event_recv_port;
++
++      return (0);
++
++fail_destroy_event_recv_port:
++
++      ps3_event_receive_port_destroy(spuisofs_spe_virq[3]);
++
++fail_free_spe_irq_2:
++
++      free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]);
++
++fail_destroy_spe_irq_2:
++
++      ps3_spe_irq_destroy(spuisofs_spe_virq[2]);
++
++fail_free_spe_irq_1:
++
++      free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]);
++
++fail_destroy_spe_irq_1:
++
++      ps3_spe_irq_destroy(spuisofs_spe_virq[1]);
++
++fail_free_spe_irq_0:
++
++      free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]);
++
++fail_destroy_spe_irq_0:
++
++      ps3_spe_irq_destroy(spuisofs_spe_virq[0]);
++
++fail_unmap_shadow:
++
++      iounmap(spuisofs_spe_shadow);
++
++fail_unmap_ls:
++
++      iounmap(spuisofs_spe_ls);
++
++fail_unmap_problem:
++
++      iounmap(spuisofs_spe_problem);
++
++fail_unmap_priv2:
++
++      iounmap(spuisofs_spe_priv2);
++
++fail_destruct_spe:
++
++      lv1_destruct_logical_spe(spuisofs_spe_id);
++
++      return (err);
++}
++
++/*
++ * spuisofs_destruct_spe
++ */
++static void spuisofs_destruct_spe(void)
++{
++      free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]);
++      ps3_spe_irq_destroy(spuisofs_spe_virq[0]);
++
++      free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]);
++      ps3_spe_irq_destroy(spuisofs_spe_virq[1]);
++
++      free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]);
++      ps3_spe_irq_destroy(spuisofs_spe_virq[2]);
++
++      free_irq(spuisofs_spe_virq[3], &spuisofs_spe_virq[3]);
++      ps3_event_receive_port_destroy(spuisofs_spe_virq[3]);
++
++      iounmap(spuisofs_spe_shadow);
++      iounmap(spuisofs_spe_ls);
++      iounmap(spuisofs_spe_problem);
++      iounmap(spuisofs_spe_priv2);
++
++      lv1_destruct_logical_spe(spuisofs_spe_id);
++}
++
++/*
++ * spuisofs_mount
++ */
++static struct dentry *spuisofs_mount(struct file_system_type *fs_type,
++      int flags, const char *dev_name, void *data)
++{
++      struct dentry *root;
++      int err;
++
++      err = spuisofs_create_spe();
++      if (err)
++              return ERR_PTR(err);
++
++      spuisofs_spe_app = vmalloc_user(spuisofs_spe_app_size);
++      if (!spuisofs_spe_app) {
++              err = -ENOMEM;
++              goto fail_destruct_spe;
++      }
++
++      memset(spuisofs_spe_app, 0, spuisofs_spe_app_size);
++
++      spuisofs_spe_arg1 = vmalloc_user(spuisofs_spe_arg1_size);
++      if (!spuisofs_spe_arg1) {
++              err = -ENOMEM;
++              goto fail_free_spe_app;
++      }
++
++      memset(spuisofs_spe_arg1, 0, spuisofs_spe_arg1_size);
++
++      spuisofs_spe_arg2 = vmalloc_user(spuisofs_spe_arg2_size);
++      if (!spuisofs_spe_arg2) {
++              err = -ENOMEM;
++              goto fail_free_spe_arg1;
++      }
++
++      memset(spuisofs_spe_arg2, 0, spuisofs_spe_arg2_size);
++
++      spuisofs_spe_buf = vmalloc_user(spuisofs_spe_buf_size);
++      if (!spuisofs_spe_buf) {
++              err = -ENOMEM;
++              goto fail_free_spe_arg2;
++      }
++
++      memset(spuisofs_spe_buf, 0, spuisofs_spe_buf_size);
++
++      root = mount_single(fs_type, flags, data, spuisofs_fill_super);
++      if (IS_ERR(root)) {
++              err = PTR_ERR(root);
++              goto fail_free_buf;
++      }
++
++      return (root);
++
++fail_free_buf:
++
++      vfree(spuisofs_spe_buf);
++
++fail_free_spe_arg2:
++
++      vfree(spuisofs_spe_arg2);
++
++fail_free_spe_arg1:
++
++      vfree(spuisofs_spe_arg1);
++
++fail_free_spe_app:
++
++      vfree(spuisofs_spe_app);
++
++fail_destruct_spe:
++
++      spuisofs_destruct_spe();
++
++      return ERR_PTR(err);
++}
++
++/*
++ * spuisofs_kill_sb
++ */
++static void spuisofs_kill_sb(struct super_block *sb)
++{
++      kill_litter_super(sb);
++
++      vfree(spuisofs_spe_app);
++      vfree(spuisofs_spe_arg1);
++      vfree(spuisofs_spe_arg2);
++      vfree(spuisofs_spe_buf);
++      spuisofs_destruct_spe();
++}
++
++static struct file_system_type spuisofs_type = {
++      .owner          = THIS_MODULE,
++      .name           = "spuisofs",
++      .mount          = spuisofs_mount,
++      .kill_sb        = spuisofs_kill_sb,
++};
++
++/*
++ * spuisofs_init
++ */
++static int __init spuisofs_init(void)
++{
++      int err;
++
++      spuisofs_inode_cache = kmem_cache_create("spuisofs_inode_cache",
++              sizeof(struct spuisofs_inode_info), 0, SLAB_HWCACHE_ALIGN,
++              spuisofs_init_once);
++      if (!spuisofs_inode_cache)
++              return (-ENOMEM);
++
++      err = register_filesystem(&spuisofs_type);
++      if (err)
++              goto fail_destroy_inode_cache;
++
++      return (0);
++
++fail_destroy_inode_cache:
++
++      kmem_cache_destroy(spuisofs_inode_cache);
++
++      return (err);
++}
++
++/*
++ * spuisofs_exit
++ */
++static void __exit spuisofs_exit(void)
++{
++      unregister_filesystem(&spuisofs_type);
++      kmem_cache_destroy(spuisofs_inode_cache);
++}
++
++module_init(spuisofs_init);
++module_exit(spuisofs_exit);
++
++MODULE_DESCRIPTION("PS3 spuisofs");
++MODULE_AUTHOR("glevand");
++MODULE_LICENSE("GPL");
diff --git a/0230-spuldrfs.patch b/0230-spuldrfs.patch
new file mode 100644 (file)
index 0000000..e32b33c
--- /dev/null
@@ -0,0 +1,1109 @@
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-08-16 21:28:29.947236099 +0200
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-08-16 21:30:29.967243082 +0200
+@@ -209,6 +209,13 @@
+       help
+         The isolated SPU file system is used to execute isolated SPU modules.
++config SPULDR_FS
++      tristate "PS3 isolated SPU loader file system"
++      default m
++      depends on PPC_PS3
++      help
++        The isolated SPU loader file system is used to execute isolated SPU loaders.
++
+ config PS3GELIC_UDBG
+       bool "PS3 udbg output via UDP broadcasts on Ethernet"
+       depends on PPC_PS3
+--- a/arch/powerpc/platforms/ps3/Makefile      2012-08-16 21:28:29.947236099 +0200
++++ b/arch/powerpc/platforms/ps3/Makefile      2012-08-16 21:30:54.793911193 +0200
+@@ -8,3 +8,4 @@
+ obj-y += device-init.o
+ obj-$(CONFIG_SPUISO_FS) += spuisofs.o
++obj-$(CONFIG_SPULDR_FS) += spuldrfs.o
+--- /dev/null  2013-10-07 20:20:12.741358433 +0200
++++ b/arch/powerpc/platforms/ps3/spuldrfs.c    2013-10-07 22:16:03.015096113 +0200
+@@ -0,0 +1,1083 @@
++
++/*
++ * PS3 spuldrfs
++ *
++ * Copyright (C) 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/fsnotify.h>
++#include <linux/file.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/pagemap.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++
++#include <asm/uaccess.h>
++#include <asm/ps3.h>
++#include <asm/spu.h>
++#include <asm/spu_priv1.h>
++#include <asm/lv1call.h>
++
++#define SPULDRFS_MAGIC                0x7370756c
++
++struct spe_shadow {
++      u8 padding_0140[0x0140];
++      u64 int_status_class0_RW;       /* 0x0140 */
++      u64 int_status_class1_RW;       /* 0x0148 */
++      u64 int_status_class2_RW;       /* 0x0150 */
++      u8 padding_0158[0x0610-0x0158];
++      u64 mfc_dsisr_RW;               /* 0x0610 */
++      u8 padding_0618[0x0620-0x0618];
++      u64 mfc_dar_RW;                 /* 0x0620 */
++      u8 padding_0628[0x0800-0x0628];
++      u64 mfc_dsipr_R;                /* 0x0800 */
++      u8 padding_0808[0x0810-0x0808];
++      u64 mfc_lscrr_R;                /* 0x0810 */
++      u8 padding_0818[0x0c00-0x0818];
++      u64 mfc_cer_R;                  /* 0x0c00 */
++      u8 padding_0c08[0x0f00-0x0c08];
++      u64 spe_execution_status;       /* 0x0f00 */
++      u8 padding_0f08[0x1000-0x0f08];
++};
++
++struct spuldrfs_inode_info {
++      struct inode vfs_inode;
++      unsigned long io_addr;
++      void *virt_addr;
++};
++
++struct spuldrfs_tree_descr {
++      const char *name;
++      const struct file_operations *ops;
++      umode_t mode;
++      size_t size;
++      unsigned long io_addr;
++      void *virt_addr;
++};
++
++#define SPULDRFS_I(inode)     container_of(inode, struct spuldrfs_inode_info, vfs_inode)
++
++static struct kmem_cache *spuldrfs_inode_cache;
++
++static u64 spuldrfs_spe_priv2_addr;
++static u64 spuldrfs_spe_problem_addr;
++static u64 spuldrfs_spe_ls_addr;
++static u64 spuldrfs_spe_shadow_addr;
++
++static struct spu_priv2 *spuldrfs_spe_priv2;
++static struct spu_problem *spuldrfs_spe_problem;
++static void *spuldrfs_spe_ls;
++static struct spe_shadow *spuldrfs_spe_shadow;
++static u64 spuldrfs_spe_id;
++static unsigned int spuldrfs_spe_virq[4];
++
++static void *spuldrfs_spe_metldr;
++static void *spuldrfs_spe_ldr;
++static void *spuldrfs_spe_buf1;
++static void *spuldrfs_spe_buf2;
++static void *spuldrfs_spe_buf3;
++
++static unsigned int spuldrfs_spe_slb_index;
++
++static unsigned long spuldrfs_spe_metldr_size = 1024 * 1024;
++module_param(spuldrfs_spe_metldr_size, ulong, 0);
++
++static unsigned long spuldrfs_spe_ldr_size = 1024 * 1024;
++module_param(spuldrfs_spe_ldr_size, ulong, 0);
++
++static unsigned long spuldrfs_spe_buf1_size = 1024 * 1024;
++module_param(spuldrfs_spe_buf1_size, ulong, 0);
++
++static unsigned long spuldrfs_spe_buf2_size = 1024 * 1024;
++module_param(spuldrfs_spe_buf2_size, ulong, 0);
++
++static unsigned long spuldrfs_spe_buf3_size = 1024 * 1024;
++module_param(spuldrfs_spe_buf3_size, ulong, 0);
++
++static unsigned long spuldrfs_spe_trans_notify_mask = 0x7;
++module_param(spuldrfs_spe_trans_notify_mask, ulong, 0);
++
++static unsigned long spuldrfs_spe_resource_id = 6;
++module_param(spuldrfs_spe_resource_id, ulong, 0);
++
++static int spuldrfs_spe_buf_addr_32bit = 0;
++module_param(spuldrfs_spe_buf_addr_32bit, int, 0);
++
++/*
++ * spuldrfs_spe_regs_read
++ */
++static ssize_t spuldrfs_spe_regs_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (0);
++
++      return simple_read_from_buffer(buffer, size, pos,
++              si->virt_addr, inode->i_size);
++}
++
++/*
++ * spuldrfs_spe_regs_write
++ */
++static ssize_t spuldrfs_spe_regs_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (-EFBIG);
++
++      return simple_write_to_buffer(si->virt_addr, inode->i_size,
++              pos, buffer, size);
++}
++
++/*
++ * spuldrfs_spe_regs_mmap
++ */
++static int spuldrfs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++      unsigned long size, pfn;
++
++      size = vma->vm_end - vma->vm_start;
++      pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
++      vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
++#else
++      vma->vm_flags |= VM_RESERVED | VM_IO;
++#endif
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++      return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
++}
++
++static const struct file_operations spuldrfs_spe_regs_fops = {
++      .read   = spuldrfs_spe_regs_read,
++      .write  = spuldrfs_spe_regs_write,
++      .mmap   = spuldrfs_spe_regs_mmap,
++};
++
++/*
++ * spuldrfs_spe_mem_read
++ */
++static ssize_t spuldrfs_spe_mem_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (0);
++
++      return simple_read_from_buffer(buffer, size, pos,
++              si->virt_addr, inode->i_size);
++}
++
++/*
++ * spuldrfs_spe_mem_write
++ */
++static ssize_t spuldrfs_spe_mem_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (-EFBIG);
++
++      return simple_write_to_buffer(si->virt_addr, inode->i_size,
++              pos, buffer, size);
++}
++
++/*
++ * spuldrfs_spe_mem_mmap
++ */
++static int spuldrfs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++      unsigned long size, pfn;
++
++      size = vma->vm_end - vma->vm_start;
++      pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
++      vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
++#else
++      vma->vm_flags |= VM_RESERVED | VM_IO;
++#endif
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++      return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
++}
++
++static const struct file_operations spuldrfs_spe_mem_fops = {
++      .read   = spuldrfs_spe_mem_read,
++      .write  = spuldrfs_spe_mem_write,
++      .mmap   = spuldrfs_spe_mem_mmap,
++};
++
++/*
++ * spuldrfs_mem_read
++ */
++static ssize_t spuldrfs_mem_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (0);
++
++      return simple_read_from_buffer(buffer, size, pos,
++              si->virt_addr, inode->i_size);
++}
++
++/*
++ * spuldrfs_mem_write
++ */
++static ssize_t spuldrfs_mem_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++
++      if (*pos >= inode->i_size)
++              return (-EFBIG);
++
++      return simple_write_to_buffer(si->virt_addr, inode->i_size,
++              pos, buffer, size);
++}
++
++/*
++ * spuldrfs_mem_mmap
++ */
++static int spuldrfs_mem_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
++
++      return remap_vmalloc_range(vma, si->virt_addr, 0);
++}
++
++static const struct file_operations spuldrfs_mem_fops = {
++      .read   = spuldrfs_mem_read,
++      .write  = spuldrfs_mem_write,
++      .mmap   = spuldrfs_mem_mmap,
++};
++
++/*
++ * spuldrfs_info_read
++ */
++static ssize_t spuldrfs_info_read(struct file *file, char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      char buf[256];
++      size_t len;
++      unsigned long spe_buf1_addr, spe_buf2_addr, spe_buf3_addr;
++
++      spe_buf1_addr = (unsigned long) spuldrfs_spe_buf1;
++      spe_buf2_addr = (unsigned long) spuldrfs_spe_buf2;
++      spe_buf3_addr = (unsigned long) spuldrfs_spe_buf3;
++
++      if (spuldrfs_spe_buf_addr_32bit) {
++              spe_buf1_addr &= 0xfffffffful;
++              spe_buf2_addr &= 0xfffffffful;
++              spe_buf3_addr &= 0xfffffffful;
++      }
++
++      len = sprintf(buf, "buf1 %lx\nbuf2 %lx\nbuf3 %lx",
++              spe_buf1_addr, spe_buf2_addr, spe_buf3_addr);
++
++      return simple_read_from_buffer(buffer, size, pos, buf, len);
++}
++
++static const struct file_operations spuldrfs_info_fops = {
++      .read   = spuldrfs_info_read,
++};
++
++/*
++ * spuldrfs_run_write
++ */
++static ssize_t spuldrfs_run_write(struct file *file, const char __user *buffer,
++      size_t size, loff_t *pos)
++{
++      int i, err;
++
++      if (*pos)
++              return (-EINVAL);
++
++      err = lv1_disable_logical_spe(spuldrfs_spe_id, 0);
++      if (err)
++              printk(KERN_INFO"spuldrfs: lv1_disable_logical_spe failed with %d\n", err);
++
++      err = lv1_enable_logical_spe(spuldrfs_spe_id, spuldrfs_spe_resource_id);
++      if (err) {
++              printk(KERN_INFO"spuldrfs: lv1_enable_logical_spe failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      out_be32(&spuldrfs_spe_problem->spu_runcntl_RW, SPU_RUNCNTL_ISOLATE | SPU_RUNCNTL_STOP);
++
++      /* enable interrupts */
++
++      err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 0, 0x7);
++      if (err) {
++              printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 1, 0xf);
++      if (err) {
++              printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 2, 0xf);
++      if (err) {
++              printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      err = lv1_set_spe_privilege_state_area_1_register(spuldrfs_spe_id, offsetof(struct spu_priv1, mfc_sr1_RW),
++              MFC_STATE1_RELOCATE_MASK);
++      if (err) {
++              printk(KERN_INFO"spuldrfs: lv1_set_spe_privilege_state_area_1_register failed with %d\n", err);
++              return (-ENXIO);
++      }
++
++      /* invalidate all SLB entries */
++
++      out_be64(&spuldrfs_spe_priv2->slb_invalidate_all_W, 0);
++
++      for (i = 0; i <= SLB_INDEX_MASK; i++) {
++              out_be64(&spuldrfs_spe_priv2->slb_index_W, i);
++              out_be64(&spuldrfs_spe_priv2->slb_vsid_RW, 0);
++              out_be64(&spuldrfs_spe_priv2->slb_esid_RW, 0);
++      }
++
++      out_be64(&spuldrfs_spe_priv2->spu_cfg_RW, 0);
++
++      out_be32(&spuldrfs_spe_problem->spu_mb_W, (unsigned long) spuldrfs_spe_ldr >> 32);
++      out_be32(&spuldrfs_spe_problem->spu_mb_W, (unsigned long) spuldrfs_spe_ldr);
++
++      out_be32(&spuldrfs_spe_problem->signal_notify1, (unsigned long) spuldrfs_spe_metldr >> 32);
++      out_be32(&spuldrfs_spe_problem->signal_notify2, (unsigned long) spuldrfs_spe_metldr);
++
++      out_be64(&spuldrfs_spe_priv2->spu_privcntl_RW, SPU_PRIVCNT_LOAD_REQUEST_ENABLE_MASK);
++
++      out_be32(&spuldrfs_spe_problem->spu_runcntl_RW, SPU_RUNCNTL_ISOLATE | SPU_RUNCNTL_RUNNABLE);
++
++      return (size);
++}
++
++static const struct file_operations spuldrfs_run_fops = {
++      .write  = spuldrfs_run_write,
++};
++
++/*
++ * spuldrfs_alloc_inode
++ */
++static struct inode *spuldrfs_alloc_inode(struct super_block *sb)
++{
++      struct spuldrfs_inode_info *si;
++
++      si = kmem_cache_alloc(spuldrfs_inode_cache, GFP_KERNEL);
++      if (!si)
++              return (NULL);
++
++      return (&si->vfs_inode);
++}
++ 
++/*
++ * spuldrfs_i_callback
++ */
++static void spuldrfs_i_callback(struct rcu_head *head)
++{
++      struct inode *inode = container_of(head, struct inode, i_rcu);
++
++      kmem_cache_free(spuldrfs_inode_cache, SPULDRFS_I(inode));
++}
++
++/*
++ * spuldrfs_destroy_inode
++ */
++static void spuldrfs_destroy_inode(struct inode *inode)
++{
++      call_rcu(&inode->i_rcu, spuldrfs_i_callback);
++}
++
++/*
++ * spuldrfs_init_once
++ */
++static void spuldrfs_init_once(void *p)
++{
++      struct spuldrfs_inode_info *si = p;
++
++      inode_init_once(&si->vfs_inode);
++}
++
++/*
++ * spuldrfs_new_inode
++ */
++static struct inode *spuldrfs_new_inode(struct super_block *sb, umode_t mode)
++{
++      struct inode *inode;
++      
++      inode = new_inode(sb);
++      if (!inode)
++              return (NULL);
++      
++      inode->i_ino = get_next_ino();
++      inode->i_mode = mode;
++      inode->i_uid = current_fsuid();
++      inode->i_gid = current_fsgid();
++      inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
++
++      return (inode);
++}
++
++/*
++ * spuldrfs_setattr
++ */
++static int spuldrfs_setattr(struct dentry *dentry, struct iattr *attr)
++{
++      struct inode *inode = dentry->d_inode;
++
++      setattr_copy(inode, attr);
++      mark_inode_dirty(inode);
++
++      return (0);
++}
++
++static const struct inode_operations spuldrfs_inode_ops = {
++      .setattr = spuldrfs_setattr,
++};
++
++/*
++ * spuldrfs_new_file
++ */
++static int spuldrfs_new_file(struct super_block *sb, struct dentry *dentry,
++      const struct file_operations *fops, umode_t mode, size_t size,
++      unsigned long io_addr, void *virt_addr)
++{
++      struct inode *inode;
++      struct spuldrfs_inode_info *si;
++
++      inode = spuldrfs_new_inode(sb, S_IFREG | mode);
++      if (!inode)
++              return (-ENOMEM);
++
++      inode->i_op = &spuldrfs_inode_ops;
++      inode->i_fop = fops;
++      inode->i_size = size;
++      inode->i_private = NULL;
++
++      si = SPULDRFS_I(inode);
++      si->io_addr = io_addr;
++      si->virt_addr = virt_addr;
++
++      d_add(dentry, inode);
++
++      return (0);
++}
++
++/*
++ * spuldrfs_fill_dir
++ */
++static int spuldrfs_fill_dir(struct dentry *dir,
++      const struct spuldrfs_tree_descr *files)
++{
++      struct dentry *dentry, *tmp;
++      int err;
++
++      while (files->name && files->name[0]) {
++              dentry = d_alloc_name(dir, files->name);
++              if (!dentry) {
++                      err = -ENOMEM;
++                      goto fail;
++              }
++
++              err = spuldrfs_new_file(dir->d_sb, dentry, files->ops,
++                      files->mode, files->size, files->io_addr, files->virt_addr);
++              if (err)
++                      goto fail;
++
++              files++;
++      }
++
++      return (0);
++
++fail:
++
++      list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child)
++              dput(dentry);
++
++      shrink_dcache_parent(dir);
++
++      return (err);
++}
++
++static const struct super_operations spuldrfs_super_ops = {
++      .alloc_inode    = spuldrfs_alloc_inode,
++      .destroy_inode  = spuldrfs_destroy_inode,
++      .statfs         = simple_statfs,
++};
++
++/*
++ * spuldrfs_fill_super
++ */
++static int spuldrfs_fill_super(struct super_block *sb, void *data, int silent)
++{
++      const struct spuldrfs_tree_descr root_dir_contents[] = {
++              { "priv2", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuldrfs_spe_priv2_addr, spuldrfs_spe_priv2, },
++              { "problem", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuldrfs_spe_problem_addr, spuldrfs_spe_problem, },
++              { "ls", &spuldrfs_spe_mem_fops, 0666, LS_SIZE, spuldrfs_spe_ls_addr, spuldrfs_spe_ls, },
++              { "shadow", &spuldrfs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuldrfs_spe_shadow_addr, spuldrfs_spe_shadow, },
++              { "metldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_metldr_size, 0, spuldrfs_spe_metldr, },
++              { "ldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_ldr_size, 0, spuldrfs_spe_ldr, },
++              { "buf1", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf1_size, 0, spuldrfs_spe_buf1, },
++              { "buf2", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf2_size, 0, spuldrfs_spe_buf2, },
++              { "buf3", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf3_size, 0, spuldrfs_spe_buf3, },
++              { "info", &spuldrfs_info_fops, 0444, 0, 0, NULL, },
++              { "run", &spuldrfs_run_fops, 0222, 0, 0, NULL, },
++              { },
++      };
++      struct inode *root_inode;
++      int err;
++ 
++      sb->s_maxbytes = MAX_LFS_FILESIZE;
++      sb->s_blocksize = PAGE_CACHE_SIZE;
++      sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
++      sb->s_magic = SPULDRFS_MAGIC;
++      sb->s_op = &spuldrfs_super_ops;
++      sb->s_time_gran = 1;
++ 
++      root_inode = spuldrfs_new_inode(sb, S_IFDIR | 0755);
++      if (!root_inode)
++              return (-ENOMEM);
++
++      root_inode->i_op = &simple_dir_inode_operations;
++      root_inode->i_fop = &simple_dir_operations;
++
++      /* directory inodes start off with i_nlink == 2 (for "." entry) */
++      inc_nlink(root_inode);
++
++      sb->s_root = d_make_root(root_inode);
++      if (!sb->s_root)
++              return (-ENOMEM);
++
++      err = spuldrfs_fill_dir(sb->s_root, root_dir_contents);
++      if (err)
++              return (err);
++      
++      return (0);
++}
++
++/*
++ * spuldrfs_spe_ea_to_kernel_ea
++ */
++static unsigned long spuldrfs_spe_ea_to_kernel_ea(unsigned long spe_ea)
++{
++      unsigned long kernel_ea, spe_buf1_addr, spe_buf2_addr, spe_buf3_addr;
++
++      kernel_ea = spe_ea;
++
++      if (!spuldrfs_spe_buf_addr_32bit)
++              return (kernel_ea);
++
++      spe_buf1_addr = (unsigned long) spuldrfs_spe_buf1 & 0xfffffffful;
++      spe_buf2_addr = (unsigned long) spuldrfs_spe_buf2 & 0xfffffffful;
++      spe_buf3_addr = (unsigned long) spuldrfs_spe_buf3 & 0xfffffffful;
++
++      if ((spe_ea >= spe_buf1_addr) && (spe_ea < (spe_buf1_addr + spuldrfs_spe_buf1_size)))
++              kernel_ea = (unsigned long) spuldrfs_spe_buf1 + (spe_buf1_addr - spe_ea);
++      else if ((spe_ea >= spe_buf2_addr) && (spe_ea < (spe_buf2_addr + spuldrfs_spe_buf2_size)))
++              kernel_ea = (unsigned long) spuldrfs_spe_buf2 + (spe_buf2_addr - spe_ea);
++      else if ((spe_ea >= spe_buf3_addr) && (spe_ea < (spe_buf3_addr + spuldrfs_spe_buf3_size)))
++              kernel_ea = (unsigned long) spuldrfs_spe_buf3 + (spe_buf3_addr - spe_ea);
++
++      return (kernel_ea);
++}
++
++/*
++ * spuldrfs_spe_interrupt
++ */
++static irqreturn_t spuldrfs_spe_interrupt(int irq, void *data)
++{
++      u64 status;
++      u64 ea, kernel_ea, dsisr, esid, vsid;
++      u64 puint_mb_R;
++      u32 spu_status_R;
++      u64 spe_execution_status;
++      int err;
++
++      if (irq == spuldrfs_spe_virq[0]) {
++              printk(KERN_INFO"spuldrfs: got class 0 irq\n");
++
++              err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 0, &status);
++              if (err) {
++                      printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              printk(KERN_INFO"spuldrfs: status %llx\n", status);
++
++              err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 0, status, 0);
++              if (err) {
++                      printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++      } else if (irq == spuldrfs_spe_virq[1]) {
++              printk(KERN_INFO"spuldrfs: got class 1 irq\n");
++
++              err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 1, &status);
++              if (err) {
++                      printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              printk(KERN_INFO"spuldrfs: status %llx\n", status);
++
++              if (status & CLASS1_SEGMENT_FAULT_INTR) {
++                      ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW);
++                      kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea);
++
++                      esid = (ea & ESID_MASK) | SLB_ESID_V;
++                      vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K;
++
++                      printk(KERN_INFO"spuldrfs: data segment fault at %llx (%llx)\n", ea, kernel_ea);
++
++                      out_be64(&spuldrfs_spe_priv2->slb_index_W, spuldrfs_spe_slb_index);
++                      out_be64(&spuldrfs_spe_priv2->slb_vsid_RW, vsid);
++                      out_be64(&spuldrfs_spe_priv2->slb_esid_RW, esid);
++
++                      spuldrfs_spe_slb_index++;
++                      if (spuldrfs_spe_slb_index > SLB_INDEX_MASK)
++                              spuldrfs_spe_slb_index = 0;
++              }
++
++              if (status & CLASS1_STORAGE_FAULT_INTR) {
++                      ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW);
++                      kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea);
++                      dsisr = in_be64(&spuldrfs_spe_shadow->mfc_dsisr_RW);
++
++                      printk(KERN_INFO"spuldrfs: data storage fault at %llx (%llx)\n", ea, kernel_ea);
++
++                      if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
++                              err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300);
++                              if (err) {
++                                      printk(KERN_INFO"spuldrfs: hash_page failed with %d\n", err);
++                                      goto out;
++                              }
++                      }
++              }
++
++              err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 1, status, 0);
++              if (err) {
++                      printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              /* restart DMA */
++
++              out_be64(&spuldrfs_spe_priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
++      } else if (irq == spuldrfs_spe_virq[2]) {
++              printk(KERN_INFO"spuldrfs: got class 2 irq\n");
++
++              err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 2, &status);
++              if (err) {
++                      printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++
++              printk(KERN_INFO"spuldrfs: status %llx\n", status);
++
++              if (status & CLASS2_MAILBOX_INTR) {
++                      puint_mb_R = in_be64(&spuldrfs_spe_priv2->puint_mb_R);
++
++                      printk(KERN_INFO"spuldrfs: puint_mb_R %llx\n", puint_mb_R);
++              }
++
++              if (status & CLASS2_SPU_STOP_INTR) {
++                      spu_status_R = in_be32(&spuldrfs_spe_problem->spu_status_R);
++
++                      printk(KERN_INFO"spuldrfs: spu_status_R %x\n", spu_status_R);
++              }
++
++              err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 2, status, 0);
++              if (err) {
++                      printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
++                      goto out;
++              }
++      } else if (irq == spuldrfs_spe_virq[3]) {
++              spe_execution_status = spuldrfs_spe_shadow->spe_execution_status;
++
++              printk(KERN_INFO"spuldrfs: transition notification: shadow spe_execution_status %llx\n",
++                      spe_execution_status);
++      } else {
++              printk(KERN_INFO"spuldrfs: got unknown irq %d\n", irq);
++      }
++
++out:
++
++      return (IRQ_HANDLED);
++}
++
++/*
++ * spuldrfs_create_spe
++ */
++static int spuldrfs_create_spe(void)
++{
++      u64 vas_id, junk;
++      int err;
++
++      err = lv1_get_virtual_address_space_id_of_ppe(&vas_id);
++      if (err)
++              return (-ENXIO);
++
++      printk(KERN_INFO"spuldrfs: vas id %llu\n", vas_id);
++
++      err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT,
++              PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 0,
++              &spuldrfs_spe_priv2_addr, &spuldrfs_spe_problem_addr, &spuldrfs_spe_ls_addr,
++              &junk, &spuldrfs_spe_shadow_addr, &spuldrfs_spe_id);
++      if (err)
++              return (-ENXIO);
++
++      printk(KERN_INFO"spuldrfs: spe id %llu\n", spuldrfs_spe_id);
++
++      spuldrfs_spe_priv2 = ioremap(spuldrfs_spe_priv2_addr, sizeof(struct spu_priv2));
++      if (!spuldrfs_spe_priv2) {
++              err = -ENOMEM;
++              goto fail_destruct_spe;
++      }
++
++      spuldrfs_spe_problem = ioremap(spuldrfs_spe_problem_addr, sizeof(struct spu_problem));
++      if (!spuldrfs_spe_problem) {
++              err = -ENOMEM;
++              goto fail_unmap_priv2;
++      }
++
++      spuldrfs_spe_ls = ioremap_prot(spuldrfs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE);
++      if (!spuldrfs_spe_ls) {
++              err = -ENOMEM;
++              goto fail_unmap_problem;
++      }
++
++      spuldrfs_spe_shadow = __ioremap(spuldrfs_spe_shadow_addr, sizeof(struct spe_shadow),
++              _PAGE_NO_CACHE | 3);
++      if (!spuldrfs_spe_shadow) {
++              err = -ENOMEM;
++              goto fail_unmap_ls;
++      }
++
++      err =  ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 0, &spuldrfs_spe_virq[0]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_unmap_shadow;
++      }
++
++      err = request_irq(spuldrfs_spe_virq[0], spuldrfs_spe_interrupt, 0,
++              "spuldrfs_spe_irq0", &spuldrfs_spe_virq[0]);
++      if (err)
++              goto fail_destroy_spe_irq_0;
++
++      err =  ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 1, &spuldrfs_spe_virq[1]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_free_spe_irq_0;
++      }
++
++      err = request_irq(spuldrfs_spe_virq[1], spuldrfs_spe_interrupt, 0,
++              "spuldrfs_spe_irq1", &spuldrfs_spe_virq[1]);
++      if (err)
++              goto fail_destroy_spe_irq_1;
++
++      err =  ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 2, &spuldrfs_spe_virq[2]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_free_spe_irq_1;
++      }
++
++      err = request_irq(spuldrfs_spe_virq[2], spuldrfs_spe_interrupt, 0,
++              "spuldrfs_spe_irq2", &spuldrfs_spe_virq[2]);
++      if (err)
++              goto fail_destroy_spe_irq_2;
++
++      err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuldrfs_spe_virq[3]);
++      if (err) {
++              err = -ENXIO;
++              goto fail_free_spe_irq_2;
++      }
++
++      err = lv1_set_spe_transition_notifier(spuldrfs_spe_id, spuldrfs_spe_trans_notify_mask,
++              virq_to_hw(spuldrfs_spe_virq[3]));
++      if (err) {
++              printk(KERN_INFO"spuldrfs: lv1_set_spe_transition_notifier failed with %d\n", err);
++              err = -ENXIO;
++              goto fail_destroy_event_recv_port;
++      }
++
++      err = request_irq(spuldrfs_spe_virq[3], spuldrfs_spe_interrupt, 0,
++              "spuldrfs_spe_irq3", &spuldrfs_spe_virq[3]);
++      if (err)
++              goto fail_destroy_event_recv_port;
++
++      return (0);
++
++fail_destroy_event_recv_port:
++
++      ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]);
++
++fail_free_spe_irq_2:
++
++      free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]);
++
++fail_destroy_spe_irq_2:
++
++      ps3_spe_irq_destroy(spuldrfs_spe_virq[2]);
++
++fail_free_spe_irq_1:
++
++      free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]);
++
++fail_destroy_spe_irq_1:
++
++      ps3_spe_irq_destroy(spuldrfs_spe_virq[1]);
++
++fail_free_spe_irq_0:
++
++      free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]);
++
++fail_destroy_spe_irq_0:
++
++      ps3_spe_irq_destroy(spuldrfs_spe_virq[0]);
++
++fail_unmap_shadow:
++
++      iounmap(spuldrfs_spe_shadow);
++
++fail_unmap_ls:
++
++      iounmap(spuldrfs_spe_ls);
++
++fail_unmap_problem:
++
++      iounmap(spuldrfs_spe_problem);
++
++fail_unmap_priv2:
++
++      iounmap(spuldrfs_spe_priv2);
++
++fail_destruct_spe:
++
++      lv1_destruct_logical_spe(spuldrfs_spe_id);
++
++      return (err);
++}
++
++/*
++ * spuldrfs_destruct_spe
++ */
++static void spuldrfs_destruct_spe(void)
++{
++      lv1_disable_logical_spe(spuldrfs_spe_id, 0);
++
++      free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]);
++      ps3_spe_irq_destroy(spuldrfs_spe_virq[0]);
++
++      free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]);
++      ps3_spe_irq_destroy(spuldrfs_spe_virq[1]);
++
++      free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]);
++      ps3_spe_irq_destroy(spuldrfs_spe_virq[2]);
++
++      free_irq(spuldrfs_spe_virq[3], &spuldrfs_spe_virq[3]);
++      ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]);
++
++      iounmap(spuldrfs_spe_shadow);
++      iounmap(spuldrfs_spe_ls);
++      iounmap(spuldrfs_spe_problem);
++      iounmap(spuldrfs_spe_priv2);
++
++      lv1_destruct_logical_spe(spuldrfs_spe_id);
++}
++
++/*
++ * spuldrfs_mount
++ */
++static struct dentry *spuldrfs_mount(struct file_system_type *fs_type,
++      int flags, const char *dev_name, void *data)
++{
++      struct dentry *root;
++      int err;
++
++      err = spuldrfs_create_spe();
++      if (err)
++              return ERR_PTR(err);
++
++      spuldrfs_spe_metldr = vmalloc_user(spuldrfs_spe_metldr_size);
++      if (!spuldrfs_spe_metldr) {
++              err = -ENOMEM;
++              goto fail_destruct_spe;
++      }
++
++      memset(spuldrfs_spe_metldr, 0, spuldrfs_spe_metldr_size);
++
++      spuldrfs_spe_ldr = vmalloc_user(spuldrfs_spe_ldr_size);
++      if (!spuldrfs_spe_ldr) {
++              err = -ENOMEM;
++              goto fail_free_spe_metldr;
++      }
++
++      memset(spuldrfs_spe_ldr, 0, spuldrfs_spe_ldr_size);
++
++      spuldrfs_spe_buf1 = vmalloc_user(spuldrfs_spe_buf1_size);
++      if (!spuldrfs_spe_buf1) {
++              err = -ENOMEM;
++              goto fail_free_spe_ldr;
++      }
++
++      memset(spuldrfs_spe_buf1, 0, spuldrfs_spe_buf1_size);
++
++      spuldrfs_spe_buf2 = vmalloc_user(spuldrfs_spe_buf2_size);
++      if (!spuldrfs_spe_buf2) {
++              err = -ENOMEM;
++              goto fail_free_spe_buf1;
++      }
++
++      memset(spuldrfs_spe_buf2, 0, spuldrfs_spe_buf2_size);
++
++      spuldrfs_spe_buf3 = vmalloc_user(spuldrfs_spe_buf3_size);
++      if (!spuldrfs_spe_buf3) {
++              err = -ENOMEM;
++              goto fail_free_spe_buf2;
++      }
++
++      memset(spuldrfs_spe_buf3, 0, spuldrfs_spe_buf3_size);
++
++      root = mount_single(fs_type, flags, data, spuldrfs_fill_super);
++      if (IS_ERR(root)) {
++              err = PTR_ERR(root);
++              goto fail_free_spe_buf3;
++      }
++
++      return (root);
++
++fail_free_spe_buf3:
++
++      vfree(spuldrfs_spe_buf3);
++
++fail_free_spe_buf2:
++
++      vfree(spuldrfs_spe_buf2);
++
++fail_free_spe_buf1:
++
++      vfree(spuldrfs_spe_buf1);
++
++fail_free_spe_ldr:
++
++      vfree(spuldrfs_spe_ldr);
++
++fail_free_spe_metldr:
++
++      vfree(spuldrfs_spe_metldr);
++
++fail_destruct_spe:
++
++      spuldrfs_destruct_spe();
++
++      return ERR_PTR(err);
++}
++
++/*
++ * spuldrfs_kill_sb
++ */
++static void spuldrfs_kill_sb(struct super_block *sb)
++{
++      kill_litter_super(sb);
++
++      vfree(spuldrfs_spe_metldr);
++      vfree(spuldrfs_spe_ldr);
++      vfree(spuldrfs_spe_buf1);
++      vfree(spuldrfs_spe_buf2);
++      vfree(spuldrfs_spe_buf3);
++      spuldrfs_destruct_spe();
++}
++
++static struct file_system_type spuldrfs_type = {
++      .owner          = THIS_MODULE,
++      .name           = "spuldrfs",
++      .mount          = spuldrfs_mount,
++      .kill_sb        = spuldrfs_kill_sb,
++};
++
++/*
++ * spuldrfs_init
++ */
++static int __init spuldrfs_init(void)
++{
++      int err;
++
++      spuldrfs_inode_cache = kmem_cache_create("spuldrfs_inode_cache",
++              sizeof(struct spuldrfs_inode_info), 0, SLAB_HWCACHE_ALIGN,
++              spuldrfs_init_once);
++      if (!spuldrfs_inode_cache)
++              return (-ENOMEM);
++
++      err = register_filesystem(&spuldrfs_type);
++      if (err)
++              goto fail_destroy_inode_cache;
++
++      return (0);
++
++fail_destroy_inode_cache:
++
++      kmem_cache_destroy(spuldrfs_inode_cache);
++
++      return (err);
++}
++
++/*
++ * spuldrfs_exit
++ */
++static void __exit spuldrfs_exit(void)
++{
++      unregister_filesystem(&spuldrfs_type);
++      kmem_cache_destroy(spuldrfs_inode_cache);
++}
++
++module_init(spuldrfs_init);
++module_exit(spuldrfs_exit);
++
++MODULE_DESCRIPTION("PS3 spuldrfs");
++MODULE_AUTHOR("glevand");
++MODULE_LICENSE("GPL");
diff --git a/0240-ps3lv1call.patch b/0240-ps3lv1call.patch
new file mode 100644 (file)
index 0000000..6490aff
--- /dev/null
@@ -0,0 +1,247 @@
+--- a/drivers/char/Makefile    2012-10-27 02:15:23.000000000 -0800
++++ b/drivers/char/Makefile    2012-10-27 02:16:28.000000000 -0800
+@@ -67,3 +67,4 @@
+ obj-$(CONFIG_PS3_PHYSMEM)     += ps3physmem.o
+ obj-$(CONFIG_PS3_STRGMNGR)    += ps3strgmngr.o
+ obj-$(CONFIG_PS3_ENCDEC)      += ps3encdec.o
++obj-$(CONFIG_PS3_LV1CALL)     += ps3lv1call/
+--- a/arch/powerpc/platforms/ps3/Kconfig       2012-10-27 02:11:58.000000000 -0800
++++ b/arch/powerpc/platforms/ps3/Kconfig       2012-10-27 02:13:08.000000000 -0800
+@@ -228,6 +228,12 @@
+       help
+         The isolated SPU loader file system is used to execute isolated SPU loaders.
++config PS3_LV1CALL
++      tristate "PS3 LV1 Call Driver"
++      depends on PPC_PS3
++      help
++        This driver allows you to execute LV1 calls.
++
+ config PS3GELIC_UDBG
+       bool "PS3 udbg output via UDP broadcasts on Ethernet"
+       depends on PPC_PS3
+--- /dev/null  2012-10-27 01:55:30.097760558 -0800
++++ b/drivers/char/ps3lv1call/Makefile 2012-10-27 02:10:35.000000000 -0800
+@@ -0,0 +1,4 @@
++
++ps3lv1call-y := ps3lv1call_misc.o generic_lv1call.o
++
++obj-$(CONFIG_PS3_LV1CALL) += ps3lv1call.o
+--- /dev/null  2012-10-27 01:55:30.097760558 -0800
++++ b/drivers/char/ps3lv1call/generic_lv1call.S        2012-10-27 02:08:57.000000000 -0800
+@@ -0,0 +1,54 @@
++/*
++ * PS3 Generic LV1 Call
++ *
++ * Copyright (C) 2012 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <asm/processor.h>
++#include <asm/ppc_asm.h>
++
++#define lv1call               .long 0x44000022; extsw r3, r3
++
++_GLOBAL(generic_lv1call)
++      mflr r0
++      std r0, 16(r1)
++      std r3, -8(r1)
++      std r4, -16(r1)
++      stdu r5, -24(r1)
++      mr r11, r4
++      ld r3, 0(r11)
++      ld r4, 8(r11)
++      ld r5, 16(r11)
++      ld r6, 24(r11)
++      ld r7, 32(r11)
++      ld r8, 40(r11)
++      ld r9, 48(r11)
++      ld r10, 56(r11)
++      ld r11, 16(r1)
++      lv1call
++      addi r1, r1, 24
++      ld r11, -24(r1)
++      std r4, 0(r11)
++      std r5, 8(r11)
++      std r6, 16(r11)
++      std r7, 24(r11)
++      std r8, 32(r11)
++      std r9, 40(r11)
++      std r10, 48(r11)
++      ld r0, 16(r1)
++      mtlr r0
++      blr
+--- /dev/null  2013-02-13 19:35:09.393146190 +0100
++++ b/drivers/char/ps3lv1call/ps3lv1call_misc.c        2013-02-12 20:22:36.473200805 +0100
+@@ -0,0 +1,158 @@
++/*
++ * PS3 LV1 Call
++ *
++ * Copyright (C) 2012, 2013 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mm.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++#include <linux/miscdevice.h>
++
++static struct
++{
++      int num_in_args;
++      int num_out_args;
++} ps3lv1call_args[256] = {
++      [8]     = { 0, 1 },             /* lv1_undocumented_function_8 */
++      [44]    = { 1, 0 },             /* lv1_shutdown_logical_partition */
++      [91]    = { 5, 2 },             /* lv1_read_repository_node */
++      [102]   = { 0, 1 },             /* lv1_undocumented_function_102 */
++      [105]   = { 7, 0 },             /* lv1_undocumented_function_105 */
++      [106]   = { 1, 0 },             /* lv1_undocumented_function_106 */
++      [107]   = { 6, 1 },             /* lv1_undocumented_function_107 */
++      [108]   = { 1, 5 },             /* lv1_undocumented_function_108 */
++      [109]   = { 1, 0 },             /* lv1_undocumented_function_109 */
++      [127]   = { 0, 2 },             /* lv1_get_version_info */
++      [194]   = { 6, 2 },             /* lv1_net_control */
++      [231]   = { 1, 0 },             /* lv1_undocumented_function_231 */
++      [232]   = { 0, 2 },             /* lv1_get_rtc */
++      [255]   = { 1, 0 },             /* lv1_panic */
++};
++
++extern int generic_lv1call(int num_lv1call, u64 *in_args, u64 *out_args);
++
++static ssize_t ps3lv1call_do_call(char *buf, size_t size)
++{
++      u64 num_lv1call;
++      u64 args[8];
++      s64 err;
++      int num_args, i, rv;
++
++      if (size < sizeof(u64))
++              return (-EINVAL);
++
++      if (size % sizeof(u64))
++              return (-EINVAL);
++
++      num_lv1call = *(u64 *) buf;
++      if (num_lv1call > 255)
++              return (-EINVAL);
++
++      num_args = (size - sizeof(u64)) / sizeof(u64);
++      if (num_args > 8)
++              return (-EINVAL);
++
++      memset(args, 0, sizeof(args));
++
++      for (i = 0; i < num_args; i++)
++              args[i] = *(u64 *) (buf + (i + 1) * sizeof(u64));
++
++      err = generic_lv1call(num_lv1call, args, args);
++
++      rv = sizeof(err);
++      memcpy(buf, &err, sizeof(err));
++
++      if (!err) {
++              rv += ps3lv1call_args[num_lv1call].num_out_args * sizeof(u64);
++              memcpy(buf + sizeof(err), args,
++                      ps3lv1call_args[num_lv1call].num_out_args * sizeof(u64));
++      }
++
++      return (rv);
++}
++
++static ssize_t ps3lv1call_write(struct file *file, const char __user *buf,
++    size_t size, loff_t *pos)
++{
++      char *data;
++      ssize_t rv;
++ 
++      data = simple_transaction_get(file, buf, size);
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
++      rv = ps3lv1call_do_call(data, size);
++      if (rv >= 0) {
++              simple_transaction_set(file, rv);
++              rv = size;
++      }
++
++      return (rv);
++}
++
++static ssize_t ps3lv1call_read(struct file *file, char __user *buf,
++    size_t size, loff_t *pos)
++{
++      ssize_t rv;
++
++      if (!file->private_data) {
++              rv = ps3lv1call_write(file, buf, 0, pos);
++              if (rv < 0)
++                      return (rv);
++      }
++
++      return simple_transaction_read(file, buf, size, pos);
++}
++
++static const struct file_operations ps3lv1call_fops = {
++      .owner          = THIS_MODULE,
++      .write          = ps3lv1call_write,
++      .read           = ps3lv1call_read,
++      .release        = simple_transaction_release,
++      .llseek         = default_llseek,
++};
++
++static struct miscdevice ps3lv1call_misc = {
++      .minor  = MISC_DYNAMIC_MINOR,
++      .name   = "ps3lv1call",
++      .fops   = &ps3lv1call_fops,
++};
++
++static int __init ps3lv1call_init(void)
++{
++      int err;
++
++      err = misc_register(&ps3lv1call_misc);
++
++      return (err);
++}
++
++static void __exit ps3lv1call_exit(void)
++{
++      misc_deregister(&ps3lv1call_misc);
++}
++
++module_init(ps3lv1call_init);
++module_exit(ps3lv1call_exit);
++
++MODULE_AUTHOR("glevand");
++MODULE_DESCRIPTION("PS3 LV1 Call");
++MODULE_LICENSE("GPL");
diff --git a/0250-lv1call-add-debug-console-hvcalls.patch b/0250-lv1call-add-debug-console-hvcalls.patch
new file mode 100644 (file)
index 0000000..c554c5d
--- /dev/null
@@ -0,0 +1,14 @@
+--- a/arch/powerpc/include/asm/lv1call.h       2013-02-21 14:04:37.204534305 +0100
++++ b/arch/powerpc/include/asm/lv1call.h       2013-02-21 14:12:07.299635321 +0100
+@@ -274,6 +274,11 @@
+ LV1_CALL(set_dabr,                                      2, 0,  96 )
+ LV1_CALL(undocumented_function_99,                      2, 0,  99 )
+ LV1_CALL(get_total_execution_time,                      2, 1, 103 )
++LV1_CALL(undocumented_function_105,                     7, 0, 105 )
++LV1_CALL(undocumented_function_106,                     1, 0, 106 )
++LV1_CALL(undocumented_function_107,                     6, 1, 107 )
++LV1_CALL(undocumented_function_108,                     1, 5, 108 )
++LV1_CALL(undocumented_function_109,                     1, 0, 109 )
+ LV1_CALL(undocumented_function_114,                     3, 1, 114 )
+ LV1_CALL(undocumented_function_115,                     1, 0, 115 )
+ LV1_CALL(allocate_io_segment,                           3, 1, 116 )
diff --git a/0260-udbg-lv1-console.patch b/0260-udbg-lv1-console.patch
new file mode 100644 (file)
index 0000000..cb38ef7
--- /dev/null
@@ -0,0 +1,123 @@
+--- a/arch/powerpc/platforms/ps3/Kconfig       2013-02-21 13:59:28.585037559 +0100
++++ b/arch/powerpc/platforms/ps3/Kconfig       2013-02-21 14:01:19.724894506 +0100
+@@ -246,4 +246,12 @@
+         If in doubt, say N here.
++config PS3_LV1_CONS_UDBG
++      bool "PS3 udbg output via LV1 console"
++      depends on PPC_PS3
++      help
++        Enables udbg early debugging output to LV1 console.
++
++        If in doubt, say N here.
++
+ endmenu
+--- a/arch/powerpc/platforms/ps3/Makefile      2013-02-21 14:02:49.992787219 +0100
++++ b/arch/powerpc/platforms/ps3/Makefile      2013-02-21 14:03:33.870050887 +0100
+@@ -3,6 +3,7 @@
+ obj-y += system-bus.o
+ obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o
++obj-$(CONFIG_PS3_LV1_CONS_UDBG) += lv1_cons_udbg.o
+ obj-$(CONFIG_SMP) += smp.o
+ obj-$(CONFIG_SPU_BASE) += spu.o
+ obj-y += device-init.o
+--- /dev/null  2012-11-28 15:48:49.557690341 +0100
++++ b/arch/powerpc/platforms/ps3/lv1_cons_udbg.c       2013-02-21 14:23:49.250083391 +0100
+@@ -0,0 +1,57 @@
++/*
++ * PS3 LV1 Debug Console
++ *
++ * Copyright (C) 2013 glevand <[email protected]>
++ * 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; version 2 of the License.
++ *
++ * 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.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <asm/io.h>
++#include <asm/udbg.h>
++#include <asm/lv1call.h>
++
++#define LV1_CONS_ID           1
++#define LV1_CONS_LENGTH               0xff0
++
++static int initialized = 0;
++
++static void lv1_cons_udbg_putc(char ch)
++{
++      u64 data, written;
++      int ret;
++
++      if (!initialized) {
++              ret = lv1_undocumented_function_105(LV1_CONS_ID, 0, 0,
++                      LV1_CONS_LENGTH, LV1_CONS_LENGTH, 0, 0);
++              if ((ret != 0) && (ret != -7))
++                      return;
++
++              initialized = 1;
++      }
++
++      data = ch;
++      data <<= 56;
++
++      lv1_undocumented_function_107(LV1_CONS_ID, 1, data, 0, 0, 0, &written);
++
++      /* flush to console buffer in LV1 */
++
++      lv1_undocumented_function_109(LV1_CONS_ID);
++}
++
++void __init udbg_init_ps3_lv1_cons(void)
++{
++      udbg_putc = lv1_cons_udbg_putc;
++}
+--- a/arch/powerpc/Kconfig.debug       2013-02-21 14:31:39.023621546 +0100
++++ b/arch/powerpc/Kconfig.debug       2013-02-21 14:32:47.370765998 +0100
+@@ -248,6 +248,14 @@
+         Select this to enable early debugging for the PlayStation3 via
+         UDP broadcasts sent out through the Ethernet port.
++config PPC_EARLY_DEBUG_PS3_LV1_CONS
++      bool "Early debugging through the PS3 LV1 console"
++      depends on PPC_PS3
++      select PS3_LV1_CONS_UDBG
++      help
++        Select this to enable early debugging for the PlayStation3 via
++        LV1 debug console.
++
+ config PPC_EARLY_DEBUG_OPAL_RAW
+       bool "OPAL raw console"
+       depends on HVC_OPAL
+--- a/arch/powerpc/include/asm/udbg.h  2013-02-21 14:15:53.855871374 +0100
++++ b/arch/powerpc/include/asm/udbg.h  2013-02-21 14:16:16.107616876 +0100
+@@ -54,6 +54,7 @@
+ extern void __init udbg_init_wsp(void);
+ extern void __init udbg_init_ehv_bc(void);
+ extern void __init udbg_init_ps3gelic(void);
++extern void __init udbg_init_ps3_lv1_cons(void);
+ extern void __init udbg_init_debug_opal_raw(void);
+ extern void __init udbg_init_debug_opal_hvsi(void);
+--- a/arch/powerpc/kernel/udbg.c       2013-02-21 14:14:28.283621378 +0100
++++ b/arch/powerpc/kernel/udbg.c       2013-02-21 14:15:16.431624882 +0100
+@@ -68,6 +68,8 @@
+       udbg_init_ehv_bc();
+ #elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC)
+       udbg_init_ps3gelic();
++#elif defined(CONFIG_PPC_EARLY_DEBUG_PS3_LV1_CONS)
++      udbg_init_ps3_lv1_cons();
+ #elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_RAW)
+       udbg_init_debug_opal_raw();
+ #elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI)